Windows Service can not open Windows Form - winforms

I am trying to create a Windows Service using TopShelf and within this service i want to launch a Windows Form.After i created the service and i debugged it calling ShowDialog the form does not show up:
Service
class SimpleServ {
private Task task;
private const string PATH = #"D:/out.txt";
private Logger logger;
private CancellationTokenSource src = new CancellationTokenSource();
public SimpleServ() {
logger = new Logger();
}
public void Start() {
logger.Log("Started");
this.task = Task.Run(async () => {
var fm = new Fm(logger);
while (true) {
fm.ShowDialog();
logger.Log("Just closed the dialog");
await Task.Delay(3000);
}
});
}
public void Stop() {
logger.Log("Stopped service");
}
}
Form
public partial class Fm : Form {
private Logger log;
public Fm(Logger log) {
this.log = log;
this.log.Log("From Form constructor");
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
this.log.Log("Button clicked");
this.Close();
}
}
Main
class Program {
static void Main(string[] args) {
var exitCode = HostFactory.Run(x => {
x.Service<SimpleServ>(s => {
s.ConstructUsing(h => new SimpleServ());
s.WhenStarted(h => h.Start());
s.WhenStopped(h => h.Stop());
});
x.RunAsLocalSystem();
x.SetServiceName("SimpleService");
x.SetDisplayName("Simple Service");
x.SetDescription("Simple serv description");
});
int exitCodeValue = (int)Convert.ChangeType(exitCode, exitCode.GetTypeCode());
Environment.ExitCode = exitCodeValue;
}
}
I have attached myself to the service and after it reaches the line of ShowDialog nothing happens.
Update
I have also added a Logger to log all important events and so far , it seems the form opens but i can not see it:
Logger
public class Logger {
private string path;
public Logger(string logPath=Constants.PATH) {
this.path = logPath;
}
private object #lock = new object();
public void Log(string message) {
string formattedMessage = "Date:" + DateTime.Now.ToString() + "\tMessage:" + message;
File.AppendAllLines(this.path, new string[] { formattedMessage });
}
}
The output of the file is :
Date:6/12/2019 11:19:13 AM Message:Started
Date:6/12/2019 11:19:13 AM Message:From Form constructor

In a world where Session 0 Isolation -- an important security measure to prevent Shatter attacks -- is the law of the land, you should think very carefully about any design relying on service interaction.
A best practice is to restructure your solution to have:
A service that runs in the background, independently of the
user
A conventional GUI application that interacts with the service and
can be run by any user

Related

Getting issue with the dependency injection asp core 6 with winforms creation?

Github link to sample project
static void Main()
{
ApplicationConfiguration.Initialize();
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddTransient<Form1>();
services.AddTransient<Form2>();
});
var host = builder.Build();
using (var serviceScope = host.Services.CreateScope())
{
IServiceProvider services = serviceScope.ServiceProvider;
Application.Run(services.GetRequiredService<Form1>());
}
}
Form1 is MDI MdiParent where i am injecting Form 2
public partial class Form1 : Form
{
private readonly Form2 form2;
public Form1(Form2 form2)
{
InitializeComponent();
this.form2 = form2;
}
private void form2ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.form2.MdiParent = this;
this.form2.Show();
}
}
When I Open Form2 by clicking from Menu it opens and close it by using [X] button
When i reopen it i am getting error
The form is disposed when closed.
I would suggest using a factory
static void Main() {
ApplicationConfiguration.Initialize();
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) => {
services.AddTransient<Form1>();
services.AddTransient<Form2>();
//Form2 factory delegate
services.AddSingleton<Func<Form2>>(sp => () => sp.GetRequiredService<Form2>());
});
var host = builder.Build();
using (var serviceScope = host.Services.CreateScope()) {
IServiceProvider services = serviceScope.ServiceProvider;
Application.Run(services.GetRequiredService<Form1>());
}
}
to initialize a new form every time the button is clicked.
public partial class Form1 : Form {
private readonly Func<Form2> factory;
public Form1(Func<Form2> factory) {
InitializeComponent();
this.factory = factory;
}
private void form2ToolStripMenuItem_Click(object sender, EventArgs e) {
Form2 form2 = factory();
form2.MdiParent = this;
form2.Show();
}
}

ASP.NET Core with Winforms - Saving the username once

I have a standard Hoemcontroller in ASP.NET Core MVC:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index(string user)
{
if(user != null)
{
TempData["UserName"] = user;
return View("Index", user);
}
return View("Index");
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
In the Index Action there will be a parameter sent from a winforms application. The string will contain the username of the client connecting to the website. This is the code for winforms:
public partial class Form1 : Form
{
public ChromiumWebBrowser chromeBrowser;
public Form1()
{
InitializeComponent();
InitializeChromium();
this.WindowState = FormWindowState.Maximized;
}
public void InitializeChromium()
{
CefSettings settings = new CefSettings();
Cef.Initialize(settings);
Cef.EnableHighDPISupport();
chromeBrowser = new ChromiumWebBrowser("https://localhost:5001/Home/Index/" + Environment.UserName);
this.Controls.Add(chromeBrowser);
//chromeBrowser.Dock = DockStyle.Fill;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Cef.Shutdown();
}
}
Now I am unsure on how to handle this on the webserver. I get the username inside the HomeController but in the same time when a user goes to the other pages with their controllers then the user should see only his content. Is that even possible?
It is not good practice to do authorization through a parameter in URI. For that, you should use Authentication (when the user passes his login and password) and receive a token with permissions. After that, you pass the token to the server and check permission there(using Authorize attribute for example). Example
If you are making a test project, and you don't need authentication at all, then you can pass a username everywhere you need and write some code to handle the content of every user (using headers, URI parameters, etc.)

ConnectionRequest when the app is in the background

I've tested Geofence example by cn1 where it sets local notification. When the app is closed(get destroyed), it still gives notification. But I want to get location through GPS and run connectionRequest to save them in the server. I replaced the connectionRequest code instead of LocalNotification in following code but it doesnot work. What should I do to run the connectionRequest when the app is closed(not when it is minimized but destroyed) so that once the user installs and close (destroys) it, the app sent his/her location data in the server forever untill the app is uninstalled.
Geofence gf = new Geofence("test", loc, 100, 100000);
LocationManager.getLocationManager().addGeoFencing(GeofenceListenerImpl.class, gf);
Geofence with localNotification:
public class GeofenceListenerImpl implements GeofenceListener {
#Override
public void onExit(String id) {
}
#Override
public void onEntered(String id) {
if(Display.getInstance().isMinimized()) {
Display.getInstance().callSerially(() -> {
Dialog.show("Welcome", "Thanks for arriving", "OK", null);
});
} else {
LocalNotification ln = new LocalNotification();
ln.setId("LnMessage");
ln.setAlertTitle("Welcome");
ln.setAlertBody("Thanks for arriving!");
Display.getInstance().scheduleLocalNotification(ln, 10, LocalNotification.REPEAT_NONE);
}
}
}
Why the following doesnot work? (it only work when the app is running or is minimized but not when it is destroyed.)
public class GeofenceListenerImpl implements GeofenceListener {
#Override
public void onExit(String id) {
System.out.println("geofence onExit");
}
#Override
public void onEntered(String id) {
if(Display.getInstance().isMinimized()) {
Display.getInstance().callSerially(() -> {
System.out.println("geofence isMinimized");
});
} else {
System.out.println("geofence when app is closed");
//I want to run connectionRequest here but is not working
}
}
}
PS. I've used background fetch but it only works when the app is minimized.
Update1: Demo of how I used connectionRequest outside of minimized() method...
public class GeofenceListenerImpl implements GeofenceListener {
#Override
public void onExit(String id) {
System.out.println("geofence onExit");
}
#Override
public void onEntered(String id) {
if(Display.getInstance().isMinimized()) {
Display.getInstance().callSerially(() -> {
});
} else {
System.out.println("geofence when app is closed");
Connection c = new Connection();
c.liveTrackConnectionMethod("22" , "23");
}
}
}
Connection class
public class Connection {
ArrayList<Map<String, Object>> response;
public void liveTrackConnectionMethod(String lat, String lon) {
ConnectionRequest cr = new ConnectionRequest() {
#Override
protected void readResponse(InputStream input) throws IOException {
JSONParser jSONParser = new JSONParser();
Map parser = jSONParser.parseJSON(new InputStreamReader(input));
response = null;
}
};
cr.setPost(true);
cr.setUrl("http://url.com");
cr.addArgument("userid", Preferences.get(AllUrls.userIdPreference, null));
cr.addArgument("lat", lat + "");
cr.addArgument("long", lon + "");
cr.addRequestHeader("Accept", "application/json");
NetworkManager.getInstance().addToQueueAndWait(cr);
}
}
I think an app will always return false for isMinimized() when the app is closed or minimized (i.e. not currently running in the foreground) I may be wrong about this.
Try calling your connectionRequest script outside the isMinimized(). After all, you will want to keep track of user location whether they are using the app or not.
Your first solution with LocalNotification will show users a notification by calling the else part, rather than the Dialog when they're using the app, because isMinimized() will be false.

net.pipe service host in WPF app

The contract:
[ServiceContract]
public interface IDaemonService {
[OperationContract]
void SendNotification(DaemonNotification notification);
}
The service:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class DaemonService : IDaemonService {
public DaemonService() {
}
public void SendNotification(DaemonNotification notification) {
App.NotificationWindow.Notify(notification);
}
}
In WPF app I do the following:
using (host = new ServiceHost(typeof (DaemonService), new[] {new Uri("net.pipe://localhost")})) {
host.AddServiceEndpoint(typeof (IDaemonService), new NetNamedPipeBinding(), "AkmDaemon");
host.Open();
}
This WPF app launches another app like this:
Task.Factory.StartNew(() => {
var tpm = new Process { StartInfo = { FileName = "TPM" } };
tpm.Start();
}
});
The app named TPM starts properly. Then I do attach to process in the debugging menu of Visual Studio and I see the client says that nobody is listening at the endpoint.
Here is the client:
[Export(typeof(DaemonClient))]
public class DaemonClient : IHandle<DaemonNotification> {
private readonly ChannelFactory<IDaemonService> channelFactory;
private readonly IDaemonService daemonServiceChannel;
public DaemonClient(IEventAggregator eventAggregator) {
EventAggregator = eventAggregator;
EventAggregator.Subscribe(this);
channelFactory = new ChannelFactory<IDaemonService>(new NetNamedPipeBinding(),
new EndpointAddress("net.pipe://localhost/AkmDaemon"));
daemonServiceChannel = channelFactory.CreateChannel();
}
public IEventAggregator EventAggregator { get; private set; }
public void Handle(DaemonNotification message) {
daemonServiceChannel.SendNotification(message); //Here I see that the endpoint //is not found
}
public void Close() {
channelFactory.Close();
}
}
EndpointNotFoundException There was no endpoint listening at "net.pipe://localhost/AkmDaemon"... blablabla
You are creating your ServiceHost in a using statement, so it is disposed right after the Open call. The Dispose call closes the ServiceHost.
using (host = new ServiceHost(...))
{
host.AddServiceEndpoint(...);
host.Open();
}
// ServiceHost.Dispose() called here
Just drop the using block.

"Authentication Failure" when calling a method on Remote Object in wpf

I am developing an application which uses WindowsFormsApplicationBase to enforce Single Instance. I get the following error when calling a method on a Remote object. It works fine if I don't use Single Instance approach.
System.Runtime.Remoting.RemotingException: Authentication failure ---> System.IO.IOException: Unable to read data from the transport connection: The connection was closed.
at System.Net.Security.NegoState.ProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.NegotiateStream.AuthenticateAsClient(NetworkCredential credential, String targetName, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel allowedImpersonationLevel)
at System.Runtime.Remoting.Channels.Tcp.TcpClientTransportSink.CreateAuthenticatedStream(Stream netStream, String machinePortAndSid)
Here is my Code:
public class EntryPoint
{
[STAThread]
public static void Main(string[] args)
{
SingleInstanceManager sim = new SingleInstanceManager();
sim.Run(args);
}
}
public class SingleInstanceManager : WindowsFormsApplicationBase
{
private App app;
public SingleInstanceManager()
{
IsSingleInstance = true;
}
protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
{
app = new App();
app.InitializeComponent();
app.Run();
return false;
}
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
base.OnStartupNextInstance(eventArgs);
app.Activate();
}
}
This is how I am invoking the Remoting object:
public Hashtable GetData(string[] arg1, string[] arg2)
{
IDataProvider dataProvider = (IDataProvider )Activator.GetObject(typeof(IDataProvider ), "tcp://.....");
Hashtable data = dataProvider.GetData(arg1, arg2);
return data;
}
Thanks in advance.
I found the solution myself.
I used the following to implement single instance(http://www.ai.uga.edu/mc/SingleInstance.html).
[STAThread]
static void Main() // args are OK here, of course
{
bool ok;
m = new System.Threading.Mutex(true, "YourNameHere", out ok);
if (! ok)
{
MessageBox.Show("Another instance is already running.");
return;
}
Application.Run(new Form1()); // or whatever was there
GC.KeepAlive(m); // important!
}

Resources