Click here to Skip to main content
Click here to Skip to main content

The Anti-Service, Persistence sans System.ServiceModel

, 9 Sep 2010
Rate this:
Please Sign up or sign in to vote.
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

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
    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"
    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?!)
    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...

        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)

Share

About the Author

CodingBruce
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 PinmemberbigRahn14-Sep-10 8:40 
GeneralThoughts PinmvpPIEBALDconsult8-Sep-10 16:53 
GeneralRe: Thoughts Pinmemberbruceme1019-Sep-10 3:08 
GeneralRe: Thoughts PinmvpPIEBALDconsult9-Sep-10 4:01 
GeneralRe: Thoughts Pinmemberpzand9-Sep-10 19:11 
GeneralRe: Thoughts PinmvpPete O'Hanlon14-Sep-10 7:57 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 9 Sep 2010
Article Copyright 2010 by CodingBruce
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid