Click here to Skip to main content
15,868,122 members
Articles / Desktop Programming / Windows Forms

The Anti-Service, Persistence sans System.ServiceModel

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
9 Sep 2010CPOL2 min read 21.2K   160   7   6
When you want an application to trigger on an event (timer, system, file, etc.), but you don't want the overhead of the service manager
Image 1

Introduction

I was asked to create a small reminder application for an organization I belong to. Ever hour it would prompt with a quote. My "work" answer for something like this would be to create a Windows service, make it self installing, easy peazy. But I wanted to be adventurous, because this is mostly U/I and I didn't want to mess with the service user account, how could this be done without a service? This article outlines this adventure...

Background

The basic components to this project are:

  • Windows (WinForms in this case, but WPF will work too), OnClose and Timers
  • Sockets/Listeners for forcing single instance and "killing" the application
  • InnoSetup Script

Using the Code

  • Create a new Windows project (New Project... Windows)
  • Create a timer. Drag and drop a timer object, set the parameters, create a timer handler. Your project may not be on a timer, that's ok, if an event triggers the window to show/hide, that's all you want
  • Create a "Force Close" mechanism, I created a checkbox called "CanClose"
  • Override FormClosing
    C#
    private void TheMessage_FormClosing(object sender, FormClosingEventArgs e)
    {
        e.Cancel = !CanClose.Checked;
        this.Hide();
    }
  • Create a separate ForceClose() method which sets CanClose and calls "Close"
    C#
    public void ForceClose()
    {
       CanClose.Checked = true;
       Close();
    }
  • Sockets and threading. This is where it gets interesting. So I wanted the behavior that the application should be single instance. There are several good ways to do that, the mutex here on CodeProject is great. But, I also needed it to install/uninstall cleanly, which means it needs to force itself closed. To do that, I used sockets listener/client on a dedicated port. It sounds counter intuitive, but I created the client first (is that the chicken or the egg?!)
    C#
    public void Main(string[] args){
        ...
    
        bool killStream = args.Length > 0 && args[0] == "-kill";
        try
        {
            using (TcpClient clnt = new TcpClient())
            {
                clnt.Connect("localhost", 7231);
                if (clnt.Connected)
                {
                    clnt.GetStream().Write(new byte[] 
    		{ (byte)(killStream ? 0 : 1) }, 0, 1); // 0 - kill or 1 - show
                    clnt.Close();
                    return;
                }
            }
        }
        // I really hate catching an exception as logic, but there's no other way
        catch (SocketException) { } // nothing listening, not already running
    
        if (killStream) // if it's a kill... don't start
            return; 

    Finally the listener...

    C#
    TcpListener lstn = new TcpListener
            (System.Net.IPAddress.Parse("127.0.0.1"), 7231);
    lstn.Start();
    
    // So, we start a long loop listening for incoming messages
    while (true)
    {
        using (TcpClient clnt = lstn.AcceptTcpClient())
        {
            byte[] buff = new byte[1];
    
            if (1 == clnt.GetStream().Read(buff, 0, 1))
            {
                switch (buff[0])
                {
                    case 0: // Kill request
                        msgWindow.ForceClose();
                        return;
                    case 1: // Show request
                        msgWindow.Show();
                        continue;
                }
            }
        }
    }
    
  • And finally, you need an InnoSetup Script that will invoke this before attempting to re-install/uninstall, that looks like this...
    [Files]
    Source: "{Your Project}\bin\Release\AntiService.exe"; DestDir: "{app}"; 
    	Flags: ignoreversion; BeforeInstall: StopLBApp
    ...
    
    [Code]
    procedure StopLBApp();
    var
      FileName : String;
      ResultCode: Integer;
    begin
      Log('Asking any existing processes to stop now');
      FileName := ExpandConstant('{app}\AntiService.exe');
      if FileExists(FileName) then
      begin
        if not ShellExec('', FileName, '-kill', '', 
    	SW_SHOWNORMAL, ewNoWait, ResultCode) then
          MsgBox('DeinitializeSetup:' #13#13 'Execution of ''' + 
    	FileName + ''' failed. ' + SysErrorMessage(ResultCode) + '.', 
    		mbError, MB_OK);
      end;
    end;

Summary

So what you have is an application that once it's started, will stay running, and will attempt to show a window every time it's launched or on some event. Timer in my case. I hope you have enjoyed!

History

  • 9th September, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer Big Company
United States United States
My professional career began as a developer fixing bugs on Microsoft Word97 and I've been fixing bad habits ever since. Now I do R&D work writing v1 line of business applications mostly in C#/.Net.

I've been an avid pilot/instructor for 13+ years, I've built two airplanes and mostly fly gliders now for fun. I commute in an all-electric 1986 BMW 325 conversion.

I'd like to get back to my academic roots of programming 3D analysis applications to organize complex systems.

Comments and Discussions

 
GeneralMight be handy Pin
bigRahn14-Sep-10 8:40
bigRahn14-Sep-10 8:40 
GeneralThoughts Pin
PIEBALDconsult8-Sep-10 16:53
mvePIEBALDconsult8-Sep-10 16:53 
GeneralRe: Thoughts Pin
CodingBruce9-Sep-10 3:08
CodingBruce9-Sep-10 3:08 
GeneralRe: Thoughts Pin
PIEBALDconsult9-Sep-10 4:01
mvePIEBALDconsult9-Sep-10 4:01 
GeneralRe: Thoughts Pin
pzand9-Sep-10 19:11
pzand9-Sep-10 19:11 
Definitely can. We have many tasks at the office running at 15 minute intervals. Especially the scheduler in Win7/2008 is pretty darn flexible.
GeneralRe: Thoughts Pin
Pete O'Hanlon14-Sep-10 7:57
subeditorPete O'Hanlon14-Sep-10 7:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.