Log users off of server, if Access application stops running

Andy Brown
Andy Brown used Ask the Experts™
on
I have an MS Access application, running on a very locked down 2012 R2, RDP server.  If an issue occurs that can't be handled by the Access application a user message pops us with one of the options being to log-off.  However, I had an issue yesterday, where something happened and Access just exited, but it left the user with a black screen requiring another user with Admin rights to log them off through the Task Manager.  I need this to be handled without Admin involvement.

I was thinking of writing a small c# app that is loaded by the Access application on startup.  My idea was to pass it the ProcessID through a command line switch, and then have the app monitor the PID every 30 seconds or so.  If the PID disappears (ie, Access has crashed out), it simply logs the user off.  However, resources on the server are tight, so I'm trying to find the best method.

Is this a good idea, or is there a better way?  Also, if anyone has any code suggestions, that would make my life easier.

Thank you.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Shaun VermaakTechnical Specialist
Awarded 2017
Distinguished Expert 2018

Commented:
[DllImport("user32.dll", SetLastError = true)]
static extern bool ExitWindowsEx(uint uFlags, uint dwReason);

Process[] processes = Process.GetProcessesByName("ProcessName");
if (processes.Length == 0)
{
     ExitWindowsEx(0, 0);
}

Open in new window

Andy BrownDeveloper

Author

Commented:
That's great - thank you Shaun.

Do you have any suggestions on how I should run this process/function?  I was thinking that it checks every 10-30 seconds or so, but as I'm pretty new to c#, I was wondering if there was a better/leaner way of doing it.
Technical Specialist
Awarded 2017
Distinguished Expert 2018
Commented:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace SomeNameSpace
{
    partial class Service : ServiceBase
    {
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool ExitWindowsEx(uint uFlags, uint dwReason);
        private Timer timer = new Timer();
        public Service()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            int interval = 5000;
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Interval = interval;
            timer.AutoReset = true;
            timer.Enabled = true;
            timer.Start();

            base.OnStart(args);
        }

        protected override void OnContinue()
        {
            timer.Start();

            base.OnContinue();
        }

        protected override void OnPause()
        {
            timer.Stop();

            base.OnPause();
        }

        protected override void OnStop()
        {
            timer.Stop();

            base.OnStop();
        }

        protected override void OnShutdown()
        {
            timer.Stop();

            base.OnShutdown();
        }
        static void timer_Elapsed(object sender, EventArgs e)
        {
            Process[] processes = Process.GetProcessesByName("ProcessName");
            if (processes.Length == 0)
            {
                ExitWindowsEx(0, 0);
            }
        }
    }
}

Open in new window

Rowby Goren Makes an Impact on Screen and Online

Learn about longtime user Rowby Goren and his great contributions to the site. We explore his method for posing questions that are likely to yield a solution, and take a look at how his career transformed from a Hollywood writer to a website entrepreneur.

Are the users being presented with a full desktop, or a remote app? If the former, maybe try setting the access program up as a remote app instead.

https://technet.microsoft.com/en-us/library/cc753844(v=ws.10).aspx
Andy BrownDeveloper

Author

Commented:
Thank you Mal - so, I'm guessing if it crashed, they could kill it through their own Task Manager?  That said, currently the way they are working (excluding this issue), is quite good.  Are there any obvious (when you know-how), pros/cons by moving to a remote app?
Distinguished Expert 2017

Commented:
The fact that Access just blanked indicates that you are using the Access runtime and/or an .accde and that there was an unhandled error.

When you use the runtime or an .accde, it is important to have extensive error trapping.  If the user has any idea what he was doing when Access just stopped, I would try to figure out where the procedure without the error handler is and fix that.
Andy BrownDeveloper

Author

Commented:
Hi Pat - good to hear from you.

Access runtime - correct.

The error trapping is good.  We've been running now for several months and this is the first instance where it's crashed them out.  Whenever an error occurs, it gets logged and I also get an email with the ID as to where is happening and fix it - but I haven't had a single error this month (other than this one).  I also think this error has been caused by a recent attack on the server, which is another story and one we have resolved.

Thanks for the feedback though.
Distinguished Expert 2017

Commented:
I can't swear to this but FMSINC.com's Total Access Analyzer might actually point out procedures that are missing error trapping.  I don't put error trapping in every procedure because it always seems like overkill to add 10 lines of error trapping code to a procedure with 1 line of code but it really depends on what the code is doing.  So, any procedure without error trapping could ultimately cause this type of failure.
Andy BrownDeveloper

Author

Commented:
I don't think it's that Pat.  

I wrote a function that generates the error tracking mechanism that I use, which I have on every procedure (almost).  If an error isn't handled in the procedure itself, it goes to a function that updates an error log, sends me an email (with procedure ID, line number, variables etc etc) and finally looks at the type of error to see what to do.  If it can't resolve the issue, the user is presented with a message and given the option to logout.

I'm not saying the code is 100% perfect (who can), but it's pretty robust, and in the case of the issue above - I think the lack of resources, caused by the hack was the root-cause.   All I'm trying to do is set up a worst case scenario, where if the user does end up back at the Desktop because of a crash and Access exiting - the user can restart with a new session.
Andy BrownDeveloper

Author

Commented:
Thank you everyone.  This certainly set me on the right path.  However, I asked another question with regards to the best type of application (class, console, form etc.), for this to run.  For some reason, I didn't get the exact answer (probably a badly worded question), but I did get some additional code that got me to 95% of where I needed to be

https://www.experts-exchange.com/questions/29070728/Best-type-of-c-solution.html?anchor=a42385878¬ificationFollowed=200874520#a42385878

In the end, I wrote a small, hidden console app, that is called when my application loads.  It sits there and checks for the ProcessID of the application every 15 seconds.  If it is no longer running - it logs the users off.

Thanks again for all of the great help/feedback.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial