How to wait for EventHandler being triggered in UWP app? - c#

I made a simple app in UWP and I'm having trouble with the EventHandlers not being triggered... I made the same app in Core.NET and it works great if I add "Console.ReadLine()" at the end of the main function so it seems like the thread of the main function dies before EventHandlers can be triggered.
Is there a way, in UWP, for the instance to stay alive a while (ie: timeout) to give a chance for the EventHandlers to be triggered.
Note: This is an example of the problem; the real context will happen in the reception of a web request.
Here's the code:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
test();
}
private async void test() {
Client DiscoverClient;
DiscoverClient = new Client();
DiscoverClient.DeviceHandler += Client_DeviceHandler;
await DiscoverClient.DiscoverAsync();
}
static private void Client_DeviceHandler(object sender, BroadlinkDevice device)
{
int x = 0;
}
}

Instead of using
await DiscoverClient.DiscoverAsync()
you could use
_ = DiscoverClient.DiscoverAsync().Result
This enforces test() to wait for DiscoverAsync() to return.
.Result may only be used if the return type is not void, so you could just let DiscoverAsync() return a boolean when it is finished. The discard is then used to have an assignment that makes the constructor block until DiscoverAsync() is done.

I tried to integrate the library mentioned in your comment into my UWP app, it will throw 'Access Denied' exption when call method GetActiveTcpConnections(line 60 in Network.cs). Since that the event Client_DeviceHandler will not be triggerred. Please see this issue(issue#24369) in github.
This document shows how to use socket in UWP. You may refer the server sample to modify or implement the feature.
https://learn.microsoft.com/en-us/windows/uwp/networking/sockets

I tryed multiple ways found on the internet (some using sockets) but I did'nt find a working solution...
I ended up using a workaround: looping through ip searching for the device's hostname. Since the IP only changes when my router is restarded, the discovery process is rarely needed so even if it's a little on the heavy side, it's quite ok for me :-)
Thanks for you help guys

Related

UWP programming - Can't get SMS store

I'm the new one in C# and UWP development and I'm trying to create an app for win-mobile 10.
One of the tasks of this app is reading SMS messages.
On one of my xaml pages I use this code:
public sealed partial class TileSettings : Page
{
public TileSettings()
{
this.InitializeComponent();
GetMessages();
}
public async Task GetMessages()
{
ChatMessageStore store = await ChatMessageManager.RequestStoreAsync();
}
}
Problem appears when I call the ChatMessageManager.RequestStoreAsync() function. The function is called but never returns any value and never exits from this function.
I have not a special permissions of Microsoft
as a developer. Can I use this function?
I'm trying to use Google and Stackoverflow search and found code like this with RequestStoreAsync() function, but it seems that no one ever faced this particular problem
May be I have logical error in the code and don't know how to use async functions in c# :)
Waiting for your comments about this behavior.
Best Regards.
You have to need Company account. Please check it out below URL what are the benefits
https://msdn.microsoft.com/windows/uwp/packaging/app-capability-declarations
I found a problem.
There is UnauthorizedAccessException in this function.
I coudn't see this exception cause call of this task is incorrect.
I try use this code:
public TileSettings()
{
this.InitializeComponent();
cts = new CancellationTokenSource();
Task.Run(() => GetMessages(), cts.Token);
}
private async void GetMessages()
{
ChatMessageStore store;
store = await ChatMessageManager.RequestStoreAsync();
var List = store.GetMessageReader();
}
and caught this exception.
Do I need a special permissions to use this function to get sms messages?

Make main thread execute code on button press after form.show

I have a piece of code that does some calculations and then calls the form.show command. Now I have a library (the revit api) that does not allow me to store variables in a project without being in the main thread.
The logical solution for this is to get the spawned thread to call the main thread using say a producer/consumer pattern with code looking a bit like this:
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
Thread.sleep(100);
}
However when I do this the gui does not load fully (black background, no text in buttons ext.).
I have also tried doing this using the evoke method
private void BtnOK_Click(object sender, System.EventArgs e)
{
Commit();
Invoke(Commit);
}
private void Invoke(Action commit)
{
commit.Invoke();
}
However this just tells me that it's not the main thread that's executing the commit function.
Is there another way to do this or am I just making an error.
Just to be clear I have a form.show(owner) command that throws an error if it's not executed by the main thread. I also have a commit() function that must be excused by the main thread or it throws an error. The execution must wait until a button press. But the main thread polling the gui thread for changing causes the program to hang. According to my google search it' s also possible to do something involving an external event to get back into the right context but the example given was using python to invoke c# code, is there a good way to raise an external event to get back into a given thread in c#?
Edit: based on some suggestions I have created the following code:
public class ThreadManager
{
static List<ThreadAble> orders = new List<ThreadAble>();
public static bool running = false;
public static void execute(ThreadAble action)
{
orders.Add(action);
}
static System.Timers.Timer timer;
public static void RegisterAPIThreadAndHold(ExternalCommandData commandData)
{
UIApplication uiapp = commandData.Application;
uiapp.Idling += Application_Idle;
}
private static void Application_Idle(Object o,IdlingEventArgs e)
{
if (orders.Count != 0)
{
ThreadAble f = orders.First();
orders.Remove(f);
f.execute();
}
}
}
public interface ThreadAble {
void execute();
}
However this does not appear to actually run when I use it as
public override Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
Form frm = new OverviewForm(ExternalCommandData commandData);
frm.show()
ThreadManager.RegisterAPIThreadAndHold(commandData);
ThreadManager.Execute(new run_ThrowError())
where ThrowError.execute() is
Throw new Exception(" this is actually being executed" );
Your first example could work if you will replace Thread.Sleep by the System.Windows.Forms.Application.DoEvents(). It should give time to paint GUI and do not froze application completly.
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
{
System.Windows.Forms.Application.DoEvents();
// Thread.sleep(100);
}
}
But this is not perfect solution to achieve this.
Better would be calling Dispatcher.Invoke command inside your dialog to perform MainThread operations.
You can use i.e. GalaSoft library - please refer to DispatcherHelper object documentation and samples.
The two ways to do this I'm aware of are with the External Event or the Idling event.
With the idling event, you'll register it, and while it is registered, your code (in the main thread) will get a callback from Revit every time that it's not busy with something else. Often a few times per second.
Once you are in the Idling callback, then you're able to create transactions and interact with the model. So your callback checks the state of the form and decides whether there is something to do.
The External Event works similarly in terms of registration, but you're able to request a trigger of the callback.
Jeremy Tammik must have 20 posts on thebuildingcoder.typepad.com on Modeless dialog / Revit stuff.
For a simple solution to this, please refer to the Revit SDK ModelessDialog ModelessForm_ExternalEvent sample application. It demonstrates exactly what you are asking for.

Not sure if I'm using "using" correctly c# in tftp app

I'm trying to use this pre-made C# tftp server app with my windows c# form. In the authors server example, which works great, he uses a console app. When I trying porting his console example into my form app it doesn't work (no errors, just doesn't connect) and I believe my issue is in the "using" statement:
using (var server = new TftpServer())
{
server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
server.Start();
Console.Read();
}
Not sure if I understand correctly but I believe the Console.Read() blocks keeping the app from exiting. If this is the case how would I implement a equivalent with a form app. I just can't get my head around the "using". Sorry I'm new to c#.
Windows Forms will always remain open until they're explicitly closed by the user. They always have a thread reading the message queue for user input, so they won't exit the same way an unrestrained console application will. In Windows Forms, we have to worry a bit more about multithreading and concurrency than we would in console apps. It mostly comes naturally, but not always.
Because of that, you can't really use an equivalent to Console.Read() to hold off execution of the using disposal until the user requests it. If you did, your form would simply appear unresponsive.
However, you're in luck! A using block in C# is nothing more than syntactic sugar for remembering to call IDisposable.Dispose() after you're done with an object. So the equivalent to this in a Forms project could just be storing the server object in a class-wide field, then calling server.Dispose() on, say, a Button.Click event. That's, of course, just an example. You could also do it on Form.Closing if that felt more appropriate.
High-level, you want to do something like this:
Declare a field in your form class TftpServer server;.
Register a Load event and whatever you need for your server to function in your constructor.
Open your server field in the Form_Load event.
Use the server's events as you see so fit during the life of your Form. You may or may not have to worry about concurrency, but that's a matter for another question.
Call server.Dispose() in the form's Dispose event.
In essence,
class main : Form
{
private TftpServer server;
public main()
{
InitializeComponent();
this.Load += main_Load;
server = new TftpServer();
server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
}
private void main_Load(object sender, EventArgs e)
{
server.Start();
}
private void server_OnReadRequest(/* I wasn't sure of the arguments here */)
{
// use the read request: give or fetch its data (depending on who defines "read")
}
private void server_OnWriteRequest(/* I wasn't sure of the arguments here */)
{
// use the write request: give or fetch its data (depending on who defines "write")
}
protected override void Dispose(bool disposing)
{
if (server != null) // since Dispose can be called multiple times
{
server.Dispose();
server = null;
}
}
}
The problem is that disposing the server is what is closing it. Keep in mind using is just syntactic sugar. The following two code chunks are [practically] equivalent:
var foo = new Foo();
try
{
foo.Do();
}
finally
{
foo.Dispose();
}
using (var foo = new Foo())
{
foo.Do();
}
You are fine blocking the main thread from exiting in a Console app, but in a Forms app it's different. The problem is not that you need to hold the thread inside the using by doing some sort of blocking operation. That would be bad, and the behavior would lock up your forms app. The problem is you don't want to use using. You want to new it up when you start the server, and then later on, on application exit, or on a stop click, explicitly dispose it with Dispose().
In a console application your TftpServer instance is listening until the thread exits which is only after a key is pressed which is detected by Console.Read()
In your forms app that Console.Read() isn't waiting around and so the using block finishes and that causes your server instance to fall out of scope.
So you are not exactly misusing the using but rather the intended use is not helping you at all. Take a look at using the task parallel library to let some background tasks run asynchronously.
A small note that also doubles as an answer, you could use a using block here, you just put it in your main function:
...(make your form and stuff)
using (var server = new TftpServer())
{
server.OnReadRequest += new TftpServerEventHandler(server_OnReadRequest);
server.OnWriteRequest += new TftpServerEventHandler(server_OnWriteRequest);
server.Start();
Application.Run(yourFormHere); //This blocks until the form is closed
}
Another option I forgot to mention is overriding Dispose in your Form. You probably want to do this. With this option you're guaranteed your server will be disposed (bar some event that would prevent it from being disposed either way [ie. being out of memory])

How do I generate keystrokes in a non-form application

So I have a huge program and decided I should make one of the methods run in a separate thread. So I put the method in a separate class, an activated it on my form. It seemed to worked just how I wanted it to until it got to part where it gave me this error:
SendKeys cannot run inside this application because the application
is not handling Windows messages. Either change the application to
handle messages, or use the SendKeys.SendWait method.
I tried looking for the answer online. I think I saw something about how SendKeys only works in a Form or something.
Can anyone tell me a way to simulate a keystroke without using SendKeys, OR a way to get SendKeys to work in a different, non-form thread?
Your console application needs a message loop. This is done through the Application class. You will need to call Application.Run(ApplicationContext).
class MyApplicationContext : ApplicationContext
{
[STAThread]
static void Main(string[] args)
{
// Create the MyApplicationContext, that derives from ApplicationContext,
// that manages when the application should exit.
MyApplicationContext context = new MyApplicationContext();
// Run the application with the specific context. It will exit when
// the task completes and calls Exit().
Application.Run(context);
}
Task backgroundTask;
// This is the constructor of the ApplicationContext, we do not want to
// block here.
private MyApplicationContext()
{
backgroundTask = Task.Factory.StartNew(BackgroundTask);
backgroundTask.ContinueWith(TaskComplete);
}
// This will allow the Application.Run(context) in the main function to
// unblock.
private void TaskComplete(Task src)
{
this.ExitThread();
}
//Perform your actual work here.
private void BackgroundTask()
{
//Stuff
SendKeys.Send("{RIGHT}");
//More stuff here
}
}
I Know this not an answer, but this how i used to do using ActiveX and Script
Set ws = CreateObject("WScript.Shell")
str = "Hi there... ~ Dont click your mouse while i am typing." & _
" ~~This is a send key example, using which you can send your keystrokes"
ws.Run("notepad.exe")
WScript.Sleep(1000)
For c=1 To Len(str)
WScript.Sleep(100) 'Increase the value for longer delay
ws.SendKeys Mid(str,c,1)
Next
Save this code as file.vbs and double click to execute in windows machine.

Topshelf - handling loops

Generally with services, the task you want to complete is repeated, maybe in a loop or maybe a trigger or maybe something else.
I'm using Topshelf to complete a repeated task for me, specifically I'm using the Shelf'ing functionality.
The problem I'm having is how to handle the looping of the task.
When boot strapping the service in Topshelf, you pass it a class (in this case ScheduleQueueService) and indicate which is its Start method and it's Stop method:
Example:
public class QueueBootstrapper : Bootstrapper<ScheduledQueueService>
{
public void InitializeHostedService(IServiceConfigurator<ScheduledQueueService> cfg)
{
cfg.HowToBuildService(n => new ScheduledQueueService());
cfg.SetServiceName("ScheduledQueueHandler");
cfg.WhenStarted(s => s.StartService());
cfg.WhenStopped(s => s.StopService());
}
}
But in my StartService() method I am using a while loop to repeat the task I'm running, but when I attempt to stop the service through Windows services it fails to stop and I suspect its because the StartService() method never ended when it was originally called.
Example:
public class ScheduledQueueService
{
bool QueueRunning;
public ScheduledQueueService()
{
QueueRunning = false;
}
public void StartService()
{
QueueRunning = true;
while(QueueRunning){
//do some work
}
}
public void StopService()
{
QueueRunning = false;
}
}
what is a better way of doing this?
I've considered using the .NET System.Threading.Tasks to run the work in and then maybe closing the thread on StopService()
Maybe using Quartz to repeat the task and then remove it.
Thoughts?
Generally, how I would handle this is have a Timer event, that fires off a few moments after StartService() is called. At the end of the event, I would check for a stop flag (set in StopService()), if the flag (e.g. your QueueRunning) isn't there, then I would register a single event on the Timer to happen again in a few moments.
We do something pretty similar in Topshelf itself, when polling the file system: https://github.com/Topshelf/Topshelf/blob/v2_master/src/Topshelf/FileSystem/PollingFileSystemEventProducer.cs#L80
Now that uses the internal scheduler type instead of a Timer object, but generally it's the same thing. The fiber is basically which thread to process the event on.
If you have future questions, you are also welcomed to join the Topshelf mailing list. We try to be pretty responsive on there. http://groups.google.com/group/topshelf-discuss
I was working on some similar code today I stumbled on https://stackoverflow.com/a/2033431/981 by accident and its been working like a charm for me.
I don't know about Topshelf specifically but when writing a standard windows service you want the start and stop events to complete as quickly as possible. If the start thread takes too long windows assumes that it has failed to start up, for example.
To get around this I generally use a System.Timers.Timer. This is set to call a startup method just once with a very short interval (so it runs almost immediately). This then does the bulk of the work.
In your case this could be your method that is looping. Then at the start of each loop check a global shutdown variable - if its true you quit the loop and then the program can stop.
You may need a bit more (or maybe even less) complexity than this depending on where exactly the error is but the general principle should be fine I hope.
Once again though I will disclaim that this knowledge is not based on topshelf, jsut general service development.

Categories

Resources