I have an application (WinForms). It simply grabs some data, does some work on it, then uploads the result to some cloud storage. I'm now using some third party code which handles the authentication with the cloud storage for me. This code will create a WinForm, which hosts a browser control, which pops up if the user has not already provided credentials and permission for the application to use the cloud storage. Before my program goes and does its thing, I want to make sure I'm already authenticated, hence in the Main() loop of the program (Program.cs), I want to use the third party library to authenticate with the cloud storage and block my actual code (//myCode) executing until it is done. 'Unfortunately' the library is asynchronous, and I've spent the best part of today trying to get the behavior I've just described - my question is effectively a "make async function synchronous", but the general answers I've seen around don't seem to work for me.
So I have something like this
static void Main(string[] args)
{
OAuth.LoginAuthAsync("clientID", "client_secret", ...)
//Don't want to get here until LoginAuthAsync is done
application.run(myForm); //myCode
}
But this will popup the web browser and run my code, which I want to happen only after the web OAuth.LoginAuthAsync has completed.
I'm no expert with the whole TPL libraries, but I've spent a while playing around with things I believe should work but don't. I've tried:
I've tried
Task.Run(async () =>
{
OAuth.LoginAuthAsync("123456", "asdasd", ...));
).Wait();
Which throws an error in the third party library
System.Threading.ThreadStateException: ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.
at System.Windows.Forms.WebBrowserBase..ctor(String clsidString)
at System.Windows.Forms.WebBrowser..ctor()
So then I tried
Thread tx = new Thread(() => OAuth.LoginAuthAsync("123456","asdasd", ……..));
tx.SetApartmentState(ApartmentState.STA);
tx.Start();
tx.Join();
but that just pops up the window and it disappears instantly, and //myCode executes.
Any body have any ideas what I can do here. I tried moving the method inside of myForm _Load event (that's myForm invoked from the application.run), but I have the same problem.
Many Thanks
P.S. I don't want to use any further third party libraries.
You can block and wait for the code to complete using the Wait() method on the Task returned from OAuth.LoginAuthAsync. This works in a console app but may cause a deadlock in a GUI/ASP app.
static void Main(string[] args)
{
OAuth.LoginAuthAsync("clientID", "client_secret", ...).Wait();
//Don't want to get here until LoginAuthAsync is done
application.run(myForm); //myCode
}
For GUI apps it is best to make the relevant method async and then await the call to OAuth.LoginAuthAsync however Main can't be async. If you move the method inside the myForm_Load method you should be able to do await the call:
public async void myForm_Load(object o, EventArgs e)
{
await OAuth.LoginAuthAsync("clientID", "client_secret", ...);
}
In my opinion that is the better solution.
Related
So I've achieved localhost WCF Named Pipes communication between client EXE and server EXE. I can call class methods on the server over localhost. So, it's like an IPC/RPC. However, if the server's class method takes a long time to execute, then it's best for me to throw that into a thread so that the server class method finishes and runs this thread in the background. Okay, fine, but then when the thread is finished its long task, I want to alert the client without having to use a timer on the client that would check that class method. A timer hitting a class method is a lot more inefficient than a raised event. It's like I need to raise an event on the client from the server. Is there an easy way to do this or to at least simulate it, without a lot of confusing work?
This is an answer formulated from my comment to the OP's question
You could make your WCF methods asynchonous then it's a simple matter of async/await or do away with WCF completely and use built-in async with NamedPipeClientStream (which is still await compatible). Not to mention a speed boost in the latter when doing away with verbose XML SOAP encoding
OP:
#MickyD You were right on the async/await thing now that I have studied that and implemented a test that works. That allows me to almost simulate a callback on a long running task and with minimal lines of code
e.g. to build upon the OP's answer but to use async/await correctly:
Client code
private async void button1_Click(object sender, EventArgs e) // <-- note async
{
label1.Text = await client.GetDataAsync(textBox1.Text); // <-- note await. New method
}
Now you could be tempted to use Task.Run but doing so is bad because:
Task.Run is best suited for compute-bound operations which we aren't.
Task.Run will at the most use an expensive thread-pool thread
We're performing an I/O operation and as such can benefit from I/O Completion Ports and "there is no thread" philosphy of IOCP present in Task I/O bound operations. As such when we make the server call via GetDataAsync, we don't waste a thread waiting for a result.
WCF Server
Here we simulate a lengthy operation by waiting, but instead of using Sleep which isn't Task-aware, we use Task.Delay which is an awaitable operation.
Task<string> async GetDataAsync (string text)
{
await Task.Delay (Timespan.FromSeconds(5));
return text + " processed";
}
So, let's say you have a button click in your UI that does a WCF synchronous method call on the WCF service, and that synchronous method takes a long time to run. Obviously, you don't want to block the UI from updating while that long running task executes. Naturally, you might be thinking about a callback. As in, you make the call to the server, and the class method on the server spawns a thread and runs that task, and when it's done, it returns back to the client a result via a callback.
To set that all up on WCF involves many complex, confusing, poorly documented steps, actually. But there's a much easier way, and it doesn't involve WCF code at all, and doesn't involve you changing anything on the WCF service, nor editing any WCF configurations. The trick is introduced in .NET 4.5 and greater. It's called async and await. Here is an example of a button click that calls a WCF service method that takes a long time to run and then returns the result when it's finished, and yet the GUI doesn't lock up and can handle other events.
1. First, to simulate a slow task, edit your WCF service project's shared class method and add this line in before the return result so that you can simulate a 5 second pause:
Thread.Sleep(5000); // requires using System.Threading;
In my case, I put that in my GetData() method.
2. Now switch to your WCF client project. You may have a button click handler that looks like this, as an example:
private void button1_Click(object sender, EventArgs e)
{
string returnString = client.GetData(textBox1.Text));
label1.Text = returnString;
}
So, switch that with three minor changes:
a. Add using System.Threading.Tasks;.
b. Change private void... to private async void... on your button click handler.
c. Utilize await Task.Run(...) with your slow method call.
Thus, the code would look like so:
private async void button1_Click(object sender, EventArgs e)
{
// Task.Run() requires "using System.Threading.Tasks;"
string returnString = await Task.Run(() => client.GetData(textBox1.Text));
label1.Text = returnString;
}
The end result is that when you click the button in the WCF client project, the GetData() class method is called on the WCF service project in a background thread, and, when finished, it comes back to that await statement and returns the result to the variable assignment. In my case, I clicked the button and nothing happened for 5 seconds -- the label with the result string didn't change. However, the GUI wasn't locked up -- I could drag the window around, type in other fields, click other form buttons, and so on. So, it's almost like a callback event handler, but not exactly. Still, it serves the same functionality and can be used in place of a callback event handler in most cases. And it involves far less code.
I'm trying to write a console application (C# .NET) that makes use of un-managed code in a 3rd party DLL.
The external assembly makes extensive use of callbacks. The sample application I have uses Windows Forms.
The sample code (Windows Forms) makes use of ThreadPool.QueueUserWorkItem
ThreadPool.QueueUserWorkItem(new WaitCallback(ConnectToControlPanel));
It would seem that with windows forms there is a message pump that handles the callbacks. This is not the case with a console app so I need to construct my own.
I've tried adding a reset event
private static ManualResetEvent resetEvent = new ManualResetEvent(false);
static void main()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ConnectToControlPanel));
resetEvent.WaitOne();
}
private void ConnectToControlPanel()
{
//Call external function from unmanaged DLL
resetEvent.Set();
}
That doesn't work.
My problem seems to be very much like this one but there is no real solution other than using Win.Forms and calling Application.DoEvents()
Edit
I modified my code thus:
Task task = Task.Factory.StartNew(() => ConnectToControlPanel());
while (task.Status != TaskStatus.RanToCompletion)
{
Application.DoEvents();
Thread.Sleep(500);
}
This works, the callbacks get called on progress and the result is successful rather than a timeout.
It doesn't feel right though to have to run that ugly loop every time I make an external call.
I've also tried spinning up another thread that constantly calls Application.DoEvents() but that didn't work, so I'm assuming it's got something to do with running in the same thread that made the call.
I have an unmanaged DLL which I'm trying to create a .NET wrapper library for but am getting different behavior when I try and run a NUnit(v3) test over it compared to if it is just run from a button click off a WinForm app.
Background: During startup of the DLL I call its Connect() method, which ultimately causes the DLL to make a TCP connection. When the TCP connection is established I then get notified by wiring up a handler to its "Connected" event.
Once connected I then call other commands on the DLL.
In a simple test Winforms app, I have 1 button which instantiates the "DLL" and then calls the Connect() method. When the thread completes, the app sits idle for about 2 seconds, and then the "connected" event handler fires as expected. The event does not return anything.
But because the connect() is an expensive operation, and because my library is destined for a larger application, I created a ConnectAsync() method in my library and made use of the async and await keywords, and a AutoResetEvent. The ConnectAsync() method returns an instance of the "instantiated" DLL after it gets notified that the TCP connection is up from the event.
A bit of refactoring to the test WinForms app, and it works as expected.
Next step was to make an integration test using NUnit. However when the ConnectAsync() method is called from an async test, I can see the TCP connection establish on the remote application, but the event handler never fires. A day's worth of testing, searching and trial and error could not turn up why the ConnectAsync() works perfect off a simple Winforms button but not from a UnitTest.
Here is the test code
[Test()]
public async Task Test1()
{
var conn = await GetConnection();
//assert some commands on the conn
}
private async Task<TCPConnector> GetConnection()
{
return await Task.Run(() =>
{
var mre = new AutoResetEvent(false);
var ctrl = new TCPConnector();
ctrl.serverName = server;
ctrl.serverPort = serverPort;
ctrl.onConnected += () => { mre.Set(); };
ctrl.Connect();
mre.WaitOne();
return ctrl;
});
}
I know this is not strictly a question, but I'm stumped and looking for ideas to try. Or pointers as to what is different between a button click event and a NUnit test execution.
In case it means something to somebody, the dll I'm calling is an unmanaged ActveX
Update1:
If use MSTest it works! So it has something to do with NUnit's startup environment.
Update2:
Through investigations in this SO post, I by chance replicated the same behaviour without any unit testing frameworks, but instead via reg free COM. So I'm now thinking it is something to do with how the COM is activated and consumed ?
Resolution
Finally found the answer.
Credit to Chris for his answer on this question. All I had to do was add a <comInterfaceExternalProxyStub /> section to the manifest as outlined, and bingo
UPDATE 4
Ignore the last updates and the resolution. They contain misdirection and false positives, and some lack of understanding of my behalf as I worked through the whole world of COM, Regfree COM, Interop, and COM Events. The problem is still unresolved.
The key issue remains that when the COM is run under the context of a unit test the COM events do not fire. When run in a plain .exe they work just fine
My guess, without knowing what exactly the unmanaged DLL is doing, is that it is an single threaded apartment (STA) COM dll. In this threading model, COM interop will marshall all calls to the DLL to the thread that creates the object (which in your unit test is blocked waiting on the auto reset event, thus nothing happens).
The event pattern works in the Winforms app because the main UI thread is an STA thread (check the attribute on your main method) and it is pumping messages, so callbacks are allowed and locks are superseded by COM message pumping.
If this is the case, the only way to test the wrapper would be to create an STA thread, run a message pump on it, then pass a message to the thread to trigger the creation of the COM object and the connection (in other words, it's a huge pain). What's worse is that the object will behave this way in a client application as well, so unless you create an STA thread in your wrapper and marshall all calls to it, you will not be able to use it asynchronously.
As Chris mentioned, it's because specific of working with COM Interop objects in STA threads. It happens because interop objects created in STA thread could be accessed (also event call) only from that thread.
All you need is to wrap the creation of any COM Interop in a separate thread.
Something like that that:
private async Task<TCPConnector> GetConnection()
{
return await Task.Run(() =>
{
var mre = new AutoResetEvent(false);
Create(mre);
mre.WaitOne();
return ctrl;
});
}
private TCPConnector ctrl;
private void Create(AutoResetEvent mre)
{
ThreadPool.QueueUserWorkItem(o =>
{
ctrl = new TCPConnector();
ctrl.serverName = server;
ctrl.serverPort = serverPort;
ctrl.onConnected += () => { mre.Set(); };
ctrl.Connect();
});
}
For windows phone 8.1 microsoft introduced some methods, which end with AndContinue. These methods suspend the app to execute and process user input. Afterwards they call a Continue...-method with a object containing the result of the operation.
A example would be WebAuthenticationBroker.AuthenticateAndContinue, which is used for OAuth.
Example code:
class Test : IWebAuthenticationContinuable
{
private void StartAuth()
{
WebAuthenticationBroker.AuthenticateAndContinue(new Uri("http://example.org/token?someInformations"),
new Uri("http://example.org/callback"), null, WebAuthenticationOptions.None);
}
private void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args)
{
WebAuthenticationResult result = args.WebAuthenticationResult;
//Do something with the result
}
}
In windows store apps the same is achived by using the WebAuthenticationBroker.AuthenticateAsync-emthod. This method is a simple async operation.
I'd like to write a AuthenticateAsync-method for windows phone using AuthenticateAndContinue. It must return a Task<WebAuthenticationResult>.
As a quite hacky approach, I thought about a Task, which is completed after the execution of ContinueWebAuthentication. If I await this task and set the result in some variable, I could access it in the async method and return it.
But I wasn't able to figure out how to implement this.
The AndContinue APIs are not asynchronous calls like the Windows Store AuthenticateAsync is. From MSDN:
When you call an AndContinue method, your app is deactivated while the operation completes in order to conserve memory. On low-memory devices, your app might even be terminated.
So, when you call an AndContinue method, your app can be terminated. When your app is restored, you'll need some way of jumping back into your async method. There's nothing like this built-in AFAIK.
It is certainly possible to create a Task<WebAuthenticationResult> using TaskCompletionSource<T>, but it won't work in this case. The AndContinue method can terminate your app, and when your app resumes, it can complete the task but there won't be any async methods waiting on it.
You could possibly have a "task cache" that is serialized on suspend/resume, and have your async methods pull from that cache, invoking other async methods only if the task isn't found. I'd say that approach is fraught with pitfalls, though, and totally not worth it.
I'm stumped with this and would appreciate any help at all!
I'm calling the Amazon api using WCF and the Visual Studio generated -asynch- methods.
There's a WPF page, with a button on. Press the button and it calls the search method in another class. (see code below)
In this other searcher class, I add the method AmazonItemSearchCompleted to handle the ItemSearchCompleted event. Then I call the asynch search function from the generated wcf code.
Client.ItemSearchCompleted += AmazonItemSearchCompleted;
Client.ItemSearchAsync(itemSearch);
This all seems to work fine. But the AmazonItemSearchCompleted method only seems to get hit after all the code in the calling form ends, ie. when I'm stepping though (no matter how long I wait for the service to respond), it gets hit at the final bracket after searchAmazon(). But by this time, it's too late to use the result of the request!!
private void button1_Click(object sender, RoutedEventArgs e)
{
searchAmazon();
} // <----- AmazonItemSearchCompleted get's hit here
private void searchAmazon()
{
var AzSearch = new AmazonSearch();
var ISBNS = new List<string>();
ISBNS.Add("0439023513");
//ISBNS.Add("9780071374323");
AzSearch.GetBookNameFromISBN(ISBNS[0]);
}
Maybe I'm missing something here, but I have no idea why the event seems to fire late?
Should I abandon the asynch methods and use the synchronous ones with a background worker?? (maybe more straightforward?)
Thanks for any help or pointers you can offer!
That's the whole point of async methods. You fire them and the code returns immediately to avoid blocking the UI until the service responds. You use the result only in the success callback (AmazonItemSearchCompleted). In the case of a WPF application if you use async methods you should be aware that the success callback could be invoked on a thread which is different than the main GUI thread and in which you should not update the controls. You need to use the Dispatcher object.