Form created in Service not showing - c#

I created a Service in C#. I needed a GUI for configuration of the Service so I added a WinForms project to my solution. My plans was to create the Form in the Service and show it in the OnStart() Method of the Service. However, it won't show. The WriteEntry() Methods of the EventLog are all firing, so my code definitely is processed. Anybody know what I'm doing wrong here?
public partial class UrlWatcherService : ServiceBase
{
private UrlWatcherForm _urlwatcherform;
private EventLog _eventLog;
private string _eventLogName = "UrlWatcherEventLog";
private string _eventLogSource = "UrlWatcherSource";
public UrlWatcherService()
{
InitializeComponent();
LoadVariables();
}
public void OnDebug()
{
OnStart(null);
}
private void LoadVariables()
{
_urlwatcherform = new UrlWatcherForm();
_eventLog = new EventLog();
CanPauseAndContinue = true;
if (!EventLog.SourceExists(_eventLogSource))
EventLog.CreateEventSource(_eventLogSource, _eventLogName);
_eventLog.Source = _eventLogSource;
_eventLog.Log = _eventLogName;
_eventLog.WriteEntry("Url Watcher Log Created", EventLogEntryType.Information);
}
protected override void OnStart(string[] args)
{
_eventLog.WriteEntry("Url Watcher Service Started", EventLogEntryType.Information);
_urlwatcherform.Show();
_eventLog.WriteEntry("Url Watcher Form Created", EventLogEntryType.Information);
}
protected override void OnPause()
{
base.OnPause();
_eventLog.WriteEntry("Url Watcher Service Paused", EventLogEntryType.Information);
}
protected override void OnContinue()
{
base.OnContinue();
_eventLog.WriteEntry("Url Watcher Log Continued", EventLogEntryType.Information);
}
protected override void OnStop()
{
_eventLog.WriteEntry("Url Watcher Service Stopped", EventLogEntryType.Information);
}
}
public partial class UrlWatcherForm : Form
{
public UrlWatcherForm()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, EventArgs e)
{
}
private void UrlWatcherGui_Resize(object sender, EventArgs e)
{
if (FormWindowState.Minimized == WindowState)
Hide();
}
private void UrlWatcherGui_FormClosing(object sender, FormClosingEventArgs e)
{
Hide();
e.Cancel = true;
}
private void urlWatcherNofiyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
Show();
}
}
EDIT: To clarify, if I debug it like below, the Form shows. I can put the thread to sleep but that won't let me interact with the Form anymore. But the Form definitely shows, it's just in an unresponsive state.
static void Main()
{
#if DEBUG
UrlWatcherService service = new UrlWatcherService();
service.OnDebug();
#else
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new UrlWatcherService()
};
ServiceBase.Run(ServicesToRun);
#endif
}

OK due to Steve's hint, I split the projects up. I use a merged module as per this MSDN article, and instead of referencing the GUI in the Service project, I separated them so I can put both their project outputs in the merged module. I then add the merged module to my installer and now I have the service running after install and the ability to call the form from my start menu. It's not what I originally wanted, but a very plausible alternative.
Thanks for Steve for the hint.

Related

WCF (Can't add Quartz in WCF Project)

(As I'm new to WCF)
I want to add Quartz in Windows Communication Foundation (WCF) project.
And I also wants to know that which file or method execute first after run the application.
Instead of using Quartz, I used C# Timer.
The Timer class in C# represents a Timer control that executes a code block at a specified interval of time repeatedly.
//Timer Class
public class FileJob
{
private System.Timers.Timer ProcessTimer;
public void Start()
{
try
{
ProcessTimer = new System.Timers.Timer();
ProcessTimer.AutoReset = true;
ProcessTimer.Elapsed += new System.Timers.ElapsedEventHandler(ProcessTimer_Elapsed);
ProcessTimer.Interval = 300000; //5 minutes
ProcessTimer.Start();
}
catch (Exception ex)
{ }
}
private void ProcessTimer_Elapsed(object sender, EventArgs e)
{
UploadFile();
}
}
Global.asax.cs
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
FileJob obj = new FileJob();
obj.Start();
}
}

Animated Splashscreen using Caliburn.Micro

I'm creating an application using Caliburn.Micro. The application communicates with an api during startup which is why I need to show a Splashscreen to the users. I've created my own animated Splashscreen as a Window which is activated from bootstrapper in the OnStartup method.
The startup process is managed by the splashscreens viewmodel.
When all startup related processes are finished how do I tell the bootstrapper to close the splashscreen and activate another window?
I thought about raising an event but I cannot subscribe the bootstrapper to the IEventaggregator.
I tried displaying the Splashscreen inside of a contentcontrol in the ShellView and just switch to a different vm after the loading is done. The problem here is that the splash should be displayed on a transparent, borderless window which cannot be changed after the window is created.
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
_container
.Singleton<IWindowManager, WindowManager>()
.Singleton<IEventAggregator, EventAggregator>();
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.ToList()
.ForEach(viewModelType => _container.RegisterPerRequest(
viewModelType, viewModelType.ToString(), viewModelType));
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(
CultureInfo.CurrentCulture.IetfLanguageTag
)
)
);
DisplayRootViewFor<AnimatedSplashViewModel>();
//DisplayRootViewFor<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
public class AnimatedSplashViewModel : Screen
{
private IEventAggregator _events;
private string _splashMessage;
public string SplashMessage
{
get { return _splashMessage; }
set
{
_splashMessage = value;
NotifyOfPropertyChange(() => SplashMessage);
}
}
public AnimatedSplashViewModel(IEventAggregator events)
{
_events = events;
SplashMessage = "Please wait";
// Simulation of long tasks
var worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_events.PublishOnUIThread(new SplashFinishedEvent());
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(10000);
}
}
You should either use a ShellViewModel for the root view and replace the splash screen view with a "main" view, or you could just wait to display the root view until the splash screen has been closed:
protected override void OnStartup(object sender, StartupEventArgs e)
{
Application.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var windowManager = IoC.Get<IWindowManager>();
var eventAggregator = IoC.Get<IEventAggregator>();
windowManager.ShowDialog(new AnimatedSplashViewModel(eventAggregator));
DisplayRootViewFor(typeof(ShellViewModel));
}
...
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TryClose();
}
I case anyone wondering about the final solution:
First I used the WindowManager to create a Dialog of the Splashscreen and let the SplashscreenViewModel do all the work.
Turned out this approach takes ages to load. So when I tried to execute it took around 8 seconds for the Dailog to show up. This is far too long for my impatient users.
I think this was because I used IoC to inject alot of dependecies into the SplashscreenViewModel.
windowManager.ShowDialog(new AnimatedSplashViewModel(locationEndpoint, userEndpoint, applicationEndpoint, adUser, clientInfo, locationInfo, loggedInUser));
Second approach was to create the Splashscreen as a dialog and use a BackgroundWorker for all the computing and api stuff inside the Bootstrapper.
While this worked quite fast I felt that there must be a better approach.
Third and final solution:
The Bootstrapper calls the ShellViewModel.
public Bootstrapper()
{
Initialize();
DisplayRootViewFor<ShellViewModel>();
}
In the OnInitialize method I've created a BackgroundWorker which executes all the long running tasks while displaying the SplashScreen as a Dialog using the WindowManager.
protected override void OnInitialize()
{
var windowManager = new WindowManager();
using (BackgroundWorker bw = new BackgroundWorker())
{
bw.DoWork += InitializeApplication;
bw.RunWorkerCompleted += InitializationCompleted;
bw.RunWorkerAsync();
windowManager.ShowDialog(new AnimatedSplashViewModel(_events));
}
}
The AnimatedSplashscreenViewModel now only requires one dependency which is the EventAggregator. I let it handle a custom Event named SplashMessageChangedEvent.
public class SplashMessageChangedEvent
{
public string Content { get; set; }
public bool CloseDialog { get; set; } = false;
public SplashMessageChangedEvent(string content)
{
Content = content;
}
public SplashMessageChangedEvent(bool closeDialog)
{
CloseDialog = closeDialog;
}
}
In the InitializationCompleted Event in the ShellViewModel I publish the following event to close the Dialog:
private void InitializationCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_events.PublishOnUIThread(new SplashMessageChangedEvent(true));
}
Now this final approach is much faster than the other two.
The Splashscreen is shown instantly after starting the executable.

Waiting while FileSystemWatcher is monitoring directory

I am trying to monitor a log file for changes. My code is working, and does everything it should. However, as I want this to run as a windows service and be constantly monitoring I'm not sure of the proper way to set it into a waiting state. Here is what it's doing at the moment.
public static void Main()
{
log_watcher = new FileSystemWatcher();
log_watcher.Path = Path.GetDirectoryName(pathToFile);
log_watcher.Filter = recent_file.Name;
log_watcher.NotifyFilter = NotifyFilters.LastWrite;
log_watcher.Changed += new FileSystemEventHandler(OnChanged);
log_watcher.EnableRaisingEvents = true;
//do rest of stuff OnChanged
while (true)
{
}
}
And then just a simple:
public static void OnChanged(object sender, FileSystemEventArgs e)
{
Console.WriteLine("File has changed");
}
What would be a better way in a windows service to do this?
You can start a message pump using Application.Run() from WinForms.
using System.Windows.Forms;
// The class that handles the creation of the application windows
class MyApplicationContext : ApplicationContext {
private MyApplicationContext() {
// Handle the ApplicationExit event to know when the application is exiting.
Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
log_watcher = new FileSystemWatcher();
log_watcher.Path = Path.GetDirectoryName(pathToFile);
log_watcher.Filter = recent_file.Name;
log_watcher.NotifyFilter = NotifyFilters.LastWrite;
log_watcher.Changed += new FileSystemEventHandler(OnChanged);
log_watcher.EnableRaisingEvents = true;
}
public static void OnChanged(object sender, FileSystemEventArgs e) {
Console.WriteLine("File has changed");
}
private void OnApplicationExit(object sender, EventArgs e) {
Console.WriteLine("File monitor exited.");
}
[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
// all forms are closed.
Application.Run(context);
}
}
See Run(ApplicationContext) on learn.microsoft.com.

What's the proper way to exit the application from WindowsFormsApplicationBase.OnCreateMainForm()?

Let's assume something went wrong at WindowsFormsApplicationBase.OnCreateMainForm()'s time, how do I exit from application "gently"? I want to exit like use had hit the close button, so I guess Environment.Exit() will not fit well since it terminates an application immediately and might not allow the application to clean up itself.
My code look like this:
public class MyApp : WindowsFormsApplicationBase
{
public MyApp()
{
this.IsSingleInstance = true;
}
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new splashForm();
}
protected override void OnCreateMainForm()
{
if(!do_something()) {
/* something got wrong, how do I exit application here? */
}
this.MainForm = new Form1(arg);
}
And my Main() function:
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new MyApp().Run(args);
}
I worked around this by creating an empty form which closes itself immediately in the load event handler. This allows to avoid the NoStartupFormException
public partial class SelfClosingForm : Form
{
public SelfClosingForm()
{
InitializeComponent();
}
private void SelfClosingForm_Load(object sender, EventArgs e)
{
Close();
}
}
protected override void OnCreateMainForm()
{
...
if (error)
{
//this is need to avoid the app hard crashing with NoStartupFormException
this.MainForm = new SelfClosingForm();
return;
}
...
Just use return:
protected override void OnCreateMainForm()
{
if(!do_something())
{
return;
}
// This won't be executed if '!do_something()' is true.
this.MainForm = new Form1(arg);
}
This will exit the current thread, so the MainForm property won't be set.

Windows Service only partly working

I have created a Windows Service for the very first time. It is not working. The files OnStart.txt and OnStop.txt are created but nothing else happens. I don't believe MSProcess runs. How do I go about troubleshooting?
namespace MailScan_Service
{
public partial class svMaster : ServiceBase
{
private Timer myTimer = new Timer();
public svMaster()
{
InitializeComponent();
}
ServiceController sc = new ServiceController("MailScan Service");
protected override void OnStart(string[] args)
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "OnStart.txt");
myTimer.Start();
}
protected override void OnStop()
{
if (myTimer.Enabled == true)
myTimer.Stop();
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "OnStop.txt");
}
private void myTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (!sc.Status.Equals(ServiceControllerStatus.StopPending))
{
MSSettings msSet = new MSSettings();
msSet.Load();
myTimer.Interval = msSet.ScanTimer * 60000;
MSProcess.Start();
msSet.Dispose();
}
}
}
Yet this simulator works just fine!
namespace MailScanSettings
{
public partial class FormSimService : Form
{
public FormSimService()
{
InitializeComponent();
}
private void btnGo_Click(object sender, EventArgs e)
{
tbStatus.Clear();
tbStatus.Text += "Running";
this.BackColor = Color.Green;
timer1.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
if (timer1.Enabled == true)
timer1.Stop();
this.BackColor = Color.Red;
tbStatus.Clear();
tbStatus.Text += "Stopped";
}
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
MSSettings msSet = new MSSettings();
msSet.Load();
timer1.Interval = msSet.ScanTimer * 60000;
MSProcess.Start();
msSet.Dispose();
}
}
}
I used to have to debug a lot of Windows Services and have done the same log file pot shot approach. It's painful and frustrating for sure.
Ultimately I found a tip somewhere, which is that for debugging purposes, you can drop this line:
System.Diagnostics.Debugger.Launch()
in the beginning of your OnStart method. It will pop a dialog and ask which debugger you want to attach. Since you'll already have the solution loaded in Visual Studio, just choose it and it will hit your breakpoints.
It can be tedious as well, but more productive. You end up in a productivity loop of having to stop the service, uninstall, patch the code, build, reinstall, start, debug...

Categories

Resources