I've been playing around with the Youtube V3 API and i can't seem to get it to work in a windows form. I got the sample code to work, so I know that my API key works correctly, but when i try to convert it from a console app, the code hangs on this line,
var searchListResponse = await searchListRequest.ExecuteAsync();
I haven't been able to find anything relevant to this issue, there are no compile errors or run-time errors are being thrown. Any help would be greatly appreciated!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
namespace MiddleManYTDL
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show("YouTube Data API: Search");
try
{
new Form1().Run().Wait();
}
catch (AggregateException exs)
{
foreach (var ex in exs.InnerExceptions)
{
MessageBox.Show("Error: " + ex.Message);
}
}
}
private async Task Run()
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApiKey = "My API Key",
ApplicationName = this.GetType().ToString()
});
var searchListRequest = youtubeService.Search.List("snippet");
searchListRequest.Q = "Google"; // Replace with your search term.
searchListRequest.MaxResults = 10;
MessageBox.Show("This will Display");
// Call the search.list method to retrieve results matching the specified query term.
var searchListResponse = await searchListRequest.ExecuteAsync();
MessageBox.Show("This never gets executed");
List<string> videos = new List<string>();
List<string> channels = new List<string>();
List<string> playlists = new List<string>();
// Add each result to the appropriate list, and then display the lists of
// matching videos, channels, and playlists.
foreach (var searchResult in searchListResponse.Items)
{
switch (searchResult.Id.Kind)
{
case "youtube#video":
videos.Add(String.Format("{0} ({1})", searchResult.Snippet.Title,
searchResult.Id.VideoId));
break;
case "youtube#channel":
channels.Add(String.Format("{0} ({1})", searchResult.Snippet.Title,
searchResult.Id.ChannelId));
break;
case "youtube#playlist":
playlists.Add(String.Format("{0} ({1})", searchResult.Snippet.Title,
searchResult.Id.PlaylistId));
break;
}
}
MessageBox.Show(String.Format("Videos:\n{0}\n", string.Join("\n", videos)));
MessageBox.Show(String.Format("Channels:\n{0}\n", string.Join("\n", channels)));
MessageBox.Show(String.Format("Playlists:\n{0}\n", string.Join("\n", playlists)));
}
}
}
This has nothing to do with the YouTube API as such. Your problem is in the blocking Wait() call on the following line:
new Form1().Run().Wait();
What is happening is your code gets to Run() and executes the part of the method up to the await keyword synchronously (in your case - on the UI thread). Then, while the await is in progress, the execution returns to your Form.Load handler, which immediately hits the Wait() call and blocks the UI thread until the task returned by Run() has completed. At some point after that your awaited task inside Run() finishes and the async state machine attempts to execute the rest of the Run() method on the UI thread. Now you have Wait() blocking the UI thread while it's waiting for Run() to complete and Run() waiting for the UI thread to become available so that the remainder of the async method can be executed. Neither can make any progress. This deadlock scenario is very common whenever async/await methods are mixed with the blocking Wait() or Result calls.
There are two possible fixes:
Use async/await all the way (i.e. no blocking Wait() and Result calls up in the call hierarchy)
Rewrite your Run() method so that no UI elements are accessed after the await, and use ConfigureAwait(false) to prevent await from capturing, and subsequently posting back to the synchronization context installed on your UI thread (meaning that the part of your method following the await will execute on a thread pool thread).
Personally I'd go with option #1:
private async void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show("YouTube Data API: Search");
try
{
await new Form1().Run();
}
catch (Exception ex)
{
// Exception dispatch is different with async/await,
// so you don't get the AggregateException - rather
// just the first exception which caused the task
// to fault.
MessageBox.Show("Error: " + ex.Message);
}
}
Here are some excellent blog posts which discuss the issue you are observing in detail:
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115163.aspx
Related
I have a WPF application that uses MVVM pattern. I have controls in the window that are bound to properties in the ViewModel. I have a Play button that is bound to the Play() method via an ICommand interface implementation. As the Play() method steps through, I first change some properties to alter the UI to show the user that the app is working:
IsPlaying = true;
IsNotPlaying = false;
DurationTimer.Start();
Status = $"Playing: {_playingUrl}";
FilePreview?.FileNameSet(_playingUrl, "");
FilePreview?.FilePlayStart();
When the Play button is pressed it should disable the Play button via the IsPlaying property and enable the Stop button via the IsNotPlaying property. Also, the DurationTimer should start (which displays a timer) and the Status property. These are intended, as said, to show the user that things are happening since FilePreview?.FilePlayStart(); is a blocking method and the UI locks up while processing.
However, when the Play button is pressed the UI immediately locks and, then, once the FilePlayStart() method finishes its processing, it releases and the other items become effective.
Am I missing something?
WPF, like most of UI frameworks, updates the UI only from one single Thread (any attempt to update the UI from another Thread will raise a System.InvalidOperationException).
Now, since the UI Thread is busy executing your method, it cannot update the UI at the same time.
WPF works with "bulks" of code. Once a bulk is executed, the UI takes care of all the updates. AT THE END of the execution, not in the middle.
So, if in the Execute method (or any other method executed on the UI Thread) you set 40 times "can execute = true", "can execute = false", "can execute = true", "can execute = false", actually you won't see the Button being unabled and disabled 40 times. Instead, when the method exits, THEN the UI is updated with the last value.
So, how to solve this? The Execute method should be asynchronous.
Something like:
public class Command : ICommand
{
//ICommad implementation and other stuffs
//...
public async void Execute(object parameter)
{
await DoExecute(parameter);
}
private async Task DoExecute(object parameter)
{
//do something asynchronously...
}
}
In your specific case, FilePreview?.FilePlayStart(); should be asynchronous, and you should pass this method to the Command.
You can write a general Command:
public class Command : ICommand
{
//ICommad implementation and other stuffs
//...
//pass the execution in the constructor
public Command(Func<object, Task> execution)
{
_execution = execution;
}
private Func<object, Task> _execution;
public async void Execute(object parameter)
{
await _execution(parameter);
}
private async Task DoExecute(object parameter)
{
//do something asynchronously... like await Task.Delay(2000);
}
}
You can then use it this way in the owner of the Command:
MyCommand = new Command(async parameter =>
{
IsPlaying = true;
IsNotPlaying = false;
await FilePreview?.FilePlayStartAsync();
});
As soon as the await part is entered, the execution pass to another Thread, and the current Thread (that is the UI Thread) is free to update the UI, and you will see that the Buttons are enabled/disabled as you want.
If an async version of the method is not available, you can write:
MyCommand = new Command(async parameter =>
{
IsPlaying = true;
IsNotPlaying = false;
await Task.Run(() => FilePreview?.FilePlayStart());
});
You can't perform long running operations on the UI thread, as it will block the dispatcher until it is done processing.
In cases like this, just use async/await to free the dispatcher and allow message pumping to continue.
private async void PlayCommand()
{
IsPlaying = true;
IsNotPlaying = false;
DurationTimer.Start();
Status = $"Playing: {_playingUrl}";
await Task.Run(()=>
{
FilePreview?.FileNameSet(_playingUrl, "");
FilePreview?.FilePlayStart();
});
}
I'm trying to render some html content to a bitmap in a Windows Service.
I'm using System.Windows.Controls.WebBrowser to perform the render. The basic rendering setup works as a standalone process with a WPF window hosting the control, but as a service, at least I'm not getting the LoadCompleted events to fire.
I know that I at least need a Dispatcher or other message pump looping for this WPF control. Perhaps I'm doing it right and there are just additional tricks/incompatibilities necessary for the WebBrowser control. Here's what I've got:
I believe only one Dispatcher needs to be running and that it can run for the life of the service. I believe the Dispatcher.Run() is the actual loop itself and thus needs it's own thread which it can otherwise block. And that thread needs to be [STAThread] in this scenario. Therefore, in a relevant static constructor, I have the following:
var thread = new Thread(() =>
{
dispatcher = Dispatcher.CurrentDispatcher;
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
where dispatcher is a static field. Again, I think there can only be one but I'm not sure if I'm supposed to be able use Dispatcher.CurrentDispatcher() from anywhere instead and get the right reference.
The rendering operation is as follows. I create, navigate, and dispose of the WebBrowser on dispatcher's thread, but event handler assignments and mres.Wait I think may all happen on the render request-handling operation. I had gotten The calling thread cannot access this object because a different thread owns it but now with this setup I don't.
WebBrowser wb = null;
var mres = new ManualResetEventSlim();
try
{
dispatcher.Invoke(() => { wb = new WebBrowser(); });
wb.LoadCompleted += (s, e) =>
{
// Not firing
};
try
{
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, Encoding.Unicode))
{
sw.Write(html);
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
// GO!
dispatcher.Invoke(() =>
{
try
{
wb.NavigateToStream(ms);
Debug.Assert(Dispatcher.FromThread(Thread.CurrentThread) != null);
}
catch (Exception ex)
{
// log
}
});
if (!mres.Wait(15 * 1000)) throw new TimeoutException();
}
}
catch (Exception ex)
{
// log
}
}
finally
{
dispatcher.Invoke(() => { if (wb != null) wb.Dispose(); });
}
When I run this, I get my timeout exception every time since the LoadCompleted never fires. I've tried to verify that the dispatcher is running and pumping properly. Not sure how to do that, but I hooked a few of the dispatcher's events from the static constructor and I get some printouts from that, so I think it's working.
The code does get to a wb.NavigateToStream(ms); breakpoint.
Is this bad application of Dispatcher? Is the non-firing of wb.LoadCompleted due to something else?
Thanks!
Here's a modified version of your code which works as a console app. A few points:
You need a parent window for WPF WebBrowser. It may be a hidden window like below, but it has to be physically created (i.e. have a live HWND handle). Otherwise, WB never finishes loading the document (wb.Document.readyState == "interactive"), and LoadCompleted never gets fired. I was not aware of such behavior and it is different from the WinForms version of WebBrowser control. May I ask why you picked WPF for this kind of project?
You do need to add the wb.LoadCompleted event handler on the same thread the WB control was created (the dispatcher's thread here). Internally, WPF WebBrowser is just a wrapper around apartment-threaded WebBrowser ActiveX control, which exposes its events via IConnectionPointContainer interface. The rule is, all calls to an apartment-threaded COM object must be made on (or proxied to) the thread the object was originally created on, because that's what such kind of objects expect. In that sense, IConnectionPointContainer methods are no different to other methods of WB.
A minor one, StreamWriter automatically closes the stream it's initialized with (unless explicitly told to not do so in the constructor), so there is no need to for wrapping the stream with using.
The code is ready to compile and run (it requires some extra assembly references: PresentationFramework, WindowsBase, System.Windows, System.Windows.Forms, Microsoft.mshtml).
using System;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Controls;
using System.IO;
using System.Runtime.InteropServices;
using mshtml;
namespace ConsoleWpfApp
{
class Program
{
static Dispatcher dispatcher = null;
static ManualResetEventSlim dispatcherReady = new ManualResetEventSlim();
static void StartUIThread()
{
var thread = new Thread(() =>
{
Debug.Print("UI Thread: {0}", Thread.CurrentThread.ManagedThreadId);
try
{
dispatcher = Dispatcher.CurrentDispatcher;
dispatcherReady.Set();
Dispatcher.Run();
}
catch (Exception ex)
{
Debug.Print("UI Thread exception: {0}", ex.ToString());
}
Debug.Print("UI Thread exits");
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
static void DoWork()
{
Debug.Print("Worker Thread: {0}", Thread.CurrentThread.ManagedThreadId);
dispatcherReady.Wait(); // wait for the UI tread to initialize
var mres = new ManualResetEventSlim();
WebBrowser wb = null;
Window window = null;
try
{
var ms = new MemoryStream();
using (var sw = new StreamWriter(ms, Encoding.Unicode)) // StreamWriter automatically closes the steam
{
sw.Write("<b>Hello, World!</b>");
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
// GO!
dispatcher.Invoke(() => // could do InvokeAsync here as then we wait anyway
{
Debug.Print("Invoke Thread: {0}", Thread.CurrentThread.ManagedThreadId);
// create a hidden window with WB
window = new Window()
{
Width = 0,
Height = 0,
Visibility = System.Windows.Visibility.Hidden,
WindowStyle = WindowStyle.None,
ShowInTaskbar = false,
ShowActivated = false
};
window.Content = wb = new WebBrowser();
window.Show();
// navigate
wb.LoadCompleted += (s, e) =>
{
Debug.Print("wb.LoadCompleted fired;");
mres.Set(); // singal to the Worker thread
};
wb.NavigateToStream(ms);
});
// wait for LoadCompleted
if (!mres.Wait(5 * 1000))
throw new TimeoutException();
dispatcher.Invoke(() =>
{
// Show the HTML
Console.WriteLine(((HTMLDocument)wb.Document).documentElement.outerHTML);
});
}
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
}
finally
{
dispatcher.Invoke(() =>
{
if (window != null)
window.Close();
if (wb != null)
wb.Dispose();
});
}
}
static void Main(string[] args)
{
StartUIThread();
DoWork();
dispatcher.InvokeShutdown(); // shutdown UI thread
Console.WriteLine("Work done, hit enter to exit");
Console.ReadLine();
}
}
}
Maybe the Webbrowser Control needs Desktop Interaction for rendering the content:
My feeling say that using WPF controls and in particular particulary the Webbrowser-Control (=Wrapper around the IE ActiveX control) isn't the best idea.. There are other rendering engines that might be better suited for this task: Use chrome as browser in C#?
I have a WPF application in PRISM architecture.
I have a 'Login View' that is shown in the 'Main Region' when the app loads.
When the user presses 'Login' - I connect to a WCF service, authenticate the user, and get a list of roles for that user from the service.
Then - according to the user's roles - I load different modules, using the 'Module Manager'.
Problem is - I want all the work after the 'Login' button is pressed to be done in a separate thread, because it might take time to connect to the service etc, and I don't want the UI to be frozen.
But - if I put the code to 'connect, authenticate, get roles, load modules' in a separate thread - I get an exception when I call '_moduleManager.LoadModule' that says:
The calling thread must be STA, because many UI components require this.
How can I solve this ?
I have tried different solutions.
I have tried to set the new thread's 'Apartment State = STA' and it didn't help.
I thought about saving the 'Dispatcher' object in the constructor of the View-Model, and then do 'dispatcher.Invoke' when I call 'LoadModule', but that is bad design (View-Model should not use Dispatcher, and also it is bad for testing).
Any ideas how I can solve this ??
Only the 'LoadModule' gives me grief, all the other stuff works fine.
.
[Update] - Added Code Sample :
[Export]
public class LoginViewModel : NotificationObject
{
[ImportingConstructor]
public LoginViewModel(IRegionManager regionManager, IModuleManager moduleManager)
{
this.LoginCommand = new DelegateCommand(LoginExecute, LoginCanExecute);
this._regionManager = regionManager;
this._moduleManager = moduleManager;
}
private void LoginExecute()
{
IsBusy = true; // Set this to 'true' so controls go disabled
LoginStatus = ""; // Clear the 'login status' string
Thread loginThread = new Thread(new ThreadStart(LoginWork));
loginThread.SetApartmentState(ApartmentState.STA);
loginThread.Start();
}
private void LoginWork()
{
ParamsToGetRoles param = new ParamsToGetRoles
{
Username = Username,
InputtedPassword = Password
};
try
{
// Connect to the secure service, and request the user's roles
_clientSecure = new AuthenticationServiceClient("WSHttpBinding_MyService");
_clientSecure.ClientCredentials.UserName.UserName = param.Username;
_clientSecure.ClientCredentials.UserName.Password = param.InputtedPassword;
_clientSecure.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);
var local = _clientSecure.ChannelFactory.CreateChannel();
_clientSecure.GetRolesCompleted += new EventHandler<GetRolesCompletedEventArgs>(clientSecure_GetRolesCompleted);
_clientSecure.GetRolesAsync(param);
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message.ToString());
}
}
void clientSecure_GetRolesCompleted(object sender, GetRolesCompletedEventArgs e)
{
if (e.Error == null)
{
_clientSecure.Close();
LoginSuccess(e.Result.UserRoles);
}
else
{
LoginFailure("Unable to authenticate");
}
_clientSecure = null;
}
private void LoginSuccess(List<UserTypeEnum> rolesOfAuthenticatedUser)
{
LoginStatus = "Success";
if (rolesOfAuthenticatedUser.Contains(UserTypeEnum.Administrator))
{
// This is what throws the exception !
// This is called by the 'EndInvoke' of the 'GetRoles' operation,
// Which was called in the 'LoginWork' function which was run on a separate thread !
_moduleManager.LoadModule(WellKnownModuleNames.ModuleAdmin);
}
NavigateToMainMenu();
this.IsBusy = false;
}
}
You should attach the debugger and inspect the threads window with a breakpoint set at clientSecure_GetRolesCompleted. I'm pretty sure it is not being called from the loginThread: while LoginWork does run in the loginThread, it then adds an eventhandler to the completion event of an async operation. Async = runs in yet another thread.
So what probably happens:
LoginExecute executes in the UI thread
starts a seperate thread B to run LoginWork
calls GetRolesAsync so start a thread C (which is not STA) to get the roles
thread C eventually calls 'clientSecure_GetRolesCompleted', not thread B
So, you do not need a seperate thread for LoginWork since the actual work is already done as an async operation. To get around the loading issue, either try to make the 'get roles' thread STA, or better, use a dispatcher so LoginSuccess gets invoked on the UI thread.
Regarding my previous post: 910220 - service methods run dependently
since the code and what I meant about the problem I had with it was a bit complex, I come again with a completely changed code which explains itself better.
In client side, we have:
#define USE_ONLY_ONE_INSTANCE
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using CountTester.ServiceReference1;
namespace CountTester
{
public partial class MainPage : UserControl
{
#if USE_ONLY_ONE_INSTANCE
private readonly Service1Client _sc = new Service1Client();
#endif
public MainPage()
{
InitializeComponent();
#if USE_ONLY_ONE_INSTANCE
_sc.CountCompleted += OnCountCompleted;
#endif
}
void OnCountCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
throw new Exception(string.Format("Count Error {0}", e.Error));
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 100; i++)
{
#if USE_ONLY_ONE_INSTANCE
_sc.CountAsync(i);
#else
var sc = new Service1Client();
sc.CountCompleted += OnCountCompleted;
sc.CountAsync(i);
//sc.CloseAsync();
#endif
}
}
}
}
this is code behind of the XAML. In the code I call a service method 100 times. I tried both cases and get exception in both cases:
case 1: I use only one instance of the proxy for all communications with server.
case 2: I use an instance for each communication with server.
Let's see the code at server before more description:
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Text;
using System.Threading;
namespace CountTester.Web
{
[ServiceContract(Namespace = "")]
[SilverlightFaultBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service1
{
const string logFileName = #"h:\CountTester.log";
object _logLock = new object();
void log(string s)
{
lock (_logLock)
{
var streamWriter = new StreamWriter(logFileName, true, Encoding.ASCII);
streamWriter.Write(s);
streamWriter.Close();
}
}
Service1()
{
//File.Delete(logFileName);
}
[OperationContract]
public void Count(int counter)
{
log(string.Format("{0}\n", counter));
Thread.Sleep(3000);
}
}
}
Count is the service method which is called. I deliberately wait 3 seconds in the method. what I expect is that the for at the client get accomplished with no delay. I expect the method gets called asynchronously and this means that first call doesn't affect on the second call.
In another words, if i call the method and it waits to get accomplished, calling it again doesn't get delayed.
While this is the case. how can I figure out that this happens? by using Tail for Windows, I find that the number which is logged is delayed in getting logged. I also figure out this when I see that I get time out exception in response to calling the service method (Count Error...). I hope I could clarify my question.
I also wonder when I see malfunctioning of the program (Exception), when I uncomment the line in which I've closed the service?
Please answer these two questions.
Question 1:
By default your IIS web service will only allow 2 simultaneous requests from the same IP address. This limitation is a default to stop DOS (Denial Of Service) attacks.
That means the subsequent 98 calls are waiting at least 3 seconds, per pair, while the previous ones complete. Eventually you will hit the default 30 second(?) server timeout long before the last requests are processed.
If you insist on running that many simultaneous requests (you shouldn't anyway), you will need to increase the default limits in your web.config file on the server. See my other answer here for more details
Question 2:
You should not close an async call immediately after starting it... the call is still in progress. That code makes no sense.
Notes:
Logging is delayed anyway, for efficiency, so don't use that as a guide.
I know there are a few answers on this topic on SO, but I can not get any of the solutions working for me. I am trying to open a new window, from an ICommand fired from within a datatemplate. Both of the following give the aforementioned error when the new window is instantiated (within "new MessageWindowP"):
Using TPL/FromCurrentSynchronizationContext Update: works
public class ChatUserCommand : ICommand
{
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(new Action<object>(CreateMessageWindow), user,CancellationToken.None, TaskCreationOptions.None,scheduler);
}
}
private void CreateMessageWindow(object o)
{
var user = (UserC)o;
var messageP = new MessageWindowP();
messageP.ViewModel.Participants.Add(user);
messageP.View.Show();
}
}
Using ThreadStart: Update: not recommended, see Jon's answer
public class ChatUserCommand : ICommand
{
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
var t = new ParameterizedThreadStart(CreateMessageWindow);
var thread = new Thread(t);
thread.SetApartmentState(ApartmentState.STA);
thread.Start(sender);
}
}
private void CreateMessageWindow(object o)
{
var user = (UserC)o;
var messageP = new MessageWindowP();
messageP.ViewModel.Participants.Add(user);
messageP.View.Show();
}
}
Thanks
EDIT. Based on the responses so far, I'd like to point out that I have also tried BeginInvoke on the current dispatcher, as well as executing the code in the original method (that's how the code started). See below:
BeginInvoke Update: not recommended see Jon's answer
public class ChatUserCommand : ICommand
{
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
Dispatcher.CurrentDispatcher.BeginInvoke(new Action<object>(CreateMessageWindow), sender);
}
}
private void CreateMessageWindow(object o)
{
var user = (UserC)o;
var messageP = new MessageWindowP();
messageP.ViewModel.Participants.Add(user);
messageP.View.Show();
}
}
In same thread Update: works if you are on UI thread already
public class ChatUserCommand : ICommand
{
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
var messageP = new MessageWindowP();
messageP.ViewModel.Participants.Add(user);
messageP.View.Show();
}
}
}
BeginInvoke, using reference to dispatcher of first/main window Update: works
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
GeneralManager.MainDispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => this.CreateMessageWindow(user)));
}
}
where GeneralManager.MainDispatcher is a reference to the Dispatcher of the first window I create:
[somewhere far far away]
mainP = new MainP();
MainDispatcher = mainP.View.Dispatcher;
I'm at a loss.
The calling thread must not only be STA, but it must also have a message loop. There's only one thread in your application that already has a message loop, and that's your main thread. So you should use Dispatcher.BeginInvoke to open your window from your main thread.
E.g. if you have a reference to your main application window (MainWindow), you can do
MainWindow.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => this.CreateMessageWindow(user)));
Update: Be careful: you cannot blindly call Dispatcher.CurrentDispatcher because it doesn't do what you think it does. The documentation says that CurrentDispatcher:
Gets the Dispatcher for the thread currently executing and creates a
new Dispatcher if one is not already associated with the thread.
That's why you must use the Dispatcher associated with an already-existing UI control (like your main window as in the example above).
With TPL you can use the StaTaskScheduler from the TPL Extras
It will run tasks on STA threads.
Only used it for COM. Never tried to run several UI threads.
You are trying to create a window from a background thread. You cannot do it due to a variety of reasons. Typically you need to create the window in the main application thread.
For your case, a simple idea would be just do it immediately (just call CreateMessageWindow inside Execute) instead of allocating a Task, because your command will definitely fire from the main (UI) thread. If you are unsure about the thread where your Execute runs, you can marshal it to the UI thread using Dispatcher.BeginInvoke().
There are really few cases when you would want your new window to run in a non-main thread. If this is really really needed in your case, you should add Dispatcher.Run(); after messageP.View.Show(); (using the second variant of the code). This starts the message loop in the new thread.
You shouldn't try to run window in TPL's thread, because these threads are typically threadpool threads, and therefore out of your control. For example, you cannot ensure they are STA (typically, they are MTA).
Edit:
from the error in your updated code, it's clear that the Execute runs in some non-UI thread. Try using Application.Current.Dispatcher instead of Dispatcher.CurrentDispatcher. (CurrentDispatcher means the dispatcher of the current thread, which may be wrong if the current thread is not the main one.)