0

I have following Windows Forms program. It has an automatically closing message box.

I have scheduled it on Windows Server 2008 R2 with “ Run whether user is logged on or not”. But this doesn’t work when scheduled like that when I logged off. But it works fine when execute the exe directly or by a batch file. The problem is with the message box.

What code change / setting change should I do to make it working with scheduler?

Note: In my real scenario I need a auto-close messagebox. Though it may seem unnecessary here.

References

  1. Task Scheduler Problem (run whether user is logged on or not)
  2. Scheduled Script Messagebox
  3. Timer just works if call MessageBox.Show()
  4. C# - System.Windows.Forms.Clipboard.GetDataObject() doesnt response
  5. Potential Pitfalls in Data and Task Parallelism
  6. Which blocking operations cause an STA thread to pump COM messages?
  7. Dealing with a blocked message loop

CODE

public partial class Form1 : Form
{
    int logNumber = 0;
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        WriteLogFunction("**");
        while (true)
        {
            WriteLogFunction("**");
            AutoClosingMessageBox.Show("A", "B", 1000);
            WriteLogFunction(">>");
        }

    }

    private void WriteLogFunction(string strMessage)
    {
        string fileName = "MYLog_" + DateTime.Now.ToString("yyyyMMMMdd");
        fileName = fileName + ".txt";
        using (StreamWriter w = File.AppendText(fileName))
        {
            w.WriteLine("\r\n{0} ..... {1} + {2}ms >>> {3}  ", logNumber.ToString(), DateTime.Now.ToLongTimeString(), DateTime.Now.Millisecond.ToString(), strMessage);
            logNumber++;
        }
    }
}
public class AutoClosingMessageBox
{
    System.Threading.Timer _timeoutTimer;
    string _caption;
    AutoClosingMessageBox(string text, string caption, int timeout)
    {
        _caption = caption;
        _timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
            null, timeout, System.Threading.Timeout.Infinite);
        MessageBox.Show(text, caption);
    }
    public static void Show(string text, string caption, int timeout)
    {
        new AutoClosingMessageBox(text, caption, timeout);
    }
    void OnTimerElapsed(object state)
    {
        IntPtr mbWnd = FindWindow(null, _caption);
        if (mbWnd != IntPtr.Zero)
            SendMessage(mbWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
        _timeoutTimer.Dispose();
    }
    const int WM_CLOSE = 0x0010;
    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
}
Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418
  • How about passing a parameter when you run from the scheduled task which signifies that the app is running unattended. Then the app can skip showing any messageboxes when the parameter exists. This has worked fine for me in the past. – DeanOC Apr 09 '14 at 02:29
  • @DeanOC Unfortunately that won't be enough for my real scenario. – LCJ Apr 09 '14 at 02:35
  • You need to explain more about your real scenario. Otherwise we are just guessing at solutions. – Blorgbeard Apr 09 '14 at 02:41
  • @Blorgbeard I am using `WebBrowser` control. In the website that I crawl, pagination works only if I use a MessageBox.... I guess due to a message pump/loop... – LCJ Apr 09 '14 at 02:43
  • If you have more info you should edit your question. – david.pfx Apr 09 '14 at 05:08

2 Answers2

2

There may not be a solution to this problem. A task set to run "whether user is logged on or not" will not run if it contains any user interactive elements. It will not create a user interface, will have no windows and no window handles, and no window-based message pump.

If your application absolutely requires a message pump, then it absolutely requires a user interface. The two go together.

david.pfx
  • 10,520
  • 3
  • 30
  • 63
0

I have achieved the required functionality (that message box was offering me) using a message pump (though this is not a preferred approach).

This is mainly based on following two - Which blocking operations cause an STA thread to pump COM messages? and How to wait for WaitHandle while serving WPF Dispatcher events?

CODE

Note: Add reference to WindowsBase dll in the project

public partial class Form1 : Form
{
    int logNumber = 0;
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        WriteLogFunction("**");

        try
        {
            while (true)
            {
                WriteLogFunction("**");
                MessagePumpHelper.PumpMessage(1000);
                WriteLogFunction(">>");
            }
        }
        catch (Exception ex)
        {
            WriteLogFunction(ex.Message);
            WriteLogFunction(ex.InnerException.ToString());
        }


    }

    private void WriteLogFunction(string strMessage)
    {
        string fileName = "MYLog_" + DateTime.Now.ToString("yyyyMMMMdd");
        fileName = fileName + ".txt";
        using (StreamWriter w = File.AppendText(fileName))
        {
            w.WriteLine("\r\n{0} ..... {1} + {2}ms >>> {3}  ", logNumber.ToString(), DateTime.Now.ToLongTimeString(), DateTime.Now.Millisecond.ToString(), strMessage);
            logNumber++;
        }
    }
}

#region WaitExt

public static class MessagePumpHelper
{
    public static void PumpMessage(int milliSecondsWait)
    {
        var dummy = new ManualResetEvent(false);
        WaitExt.WaitOneAndPump(dummy, milliSecondsWait);
    }
}

// WaitOneAndPump
public static class WaitExt
{
    public static bool WaitOneAndPump(this WaitHandle handle, int millisecondsTimeout)
    {
        using (var operationPendingMre = new ManualResetEvent(false))
        {
            var result = false;
            var startTick = Environment.TickCount;

            //using System.Windows.Threading; //From WindowsBase dll
            var dispatcher = Dispatcher.CurrentDispatcher;
            var frame = new DispatcherFrame();

            var handles = new[] { 
                    handle.SafeWaitHandle.DangerousGetHandle(), 
                    operationPendingMre.SafeWaitHandle.DangerousGetHandle() };

            // idle processing plumbing
            //using System.Windows.Threading; //From WindowsBase dll
            DispatcherOperation idleOperation = null;
            Action idleAction = () => { idleOperation = null; };
            Action enqueIdleOperation = () =>
            {
                if (idleOperation != null)
                    idleOperation.Abort();
                // post an empty operation to make sure that 
                // onDispatcherInactive will be called again
                idleOperation = dispatcher.BeginInvoke(
                    idleAction,
                    DispatcherPriority.ApplicationIdle);
            };

            // timeout plumbing
            Func<uint> getTimeout;
            if (Timeout.Infinite == millisecondsTimeout)
                getTimeout = () => INFINITE;
            else
                getTimeout = () => (uint)Math.Max(0, millisecondsTimeout + startTick - Environment.TickCount);

            //using System.Windows.Threading; //From WindowsBase dll
            DispatcherHookEventHandler onOperationPosted = (s, e) =>
            {
                // this may occur on a random thread,
                // trigger a helper event and 
                // unblock MsgWaitForMultipleObjectsEx inside onDispatcherInactive
                operationPendingMre.Set();
            };

            //using System.Windows.Threading; //From WindowsBase dll
            DispatcherHookEventHandler onOperationCompleted = (s, e) =>
            {
                // this should be fired on the Dispather thread
                Debug.Assert(Thread.CurrentThread == dispatcher.Thread);

                // do an instant handle check
                var nativeResult = WaitForSingleObject(handles[0], 0);
                if (nativeResult == WAIT_OBJECT_0)
                    result = true;
                else if (nativeResult == WAIT_ABANDONED_0)
                    throw new AbandonedMutexException(-1, handle);
                else if (getTimeout() == 0)
                    result = false;
                else if (nativeResult == WAIT_TIMEOUT)
                    return;
                else
                    throw new InvalidOperationException("WaitForSingleObject");

                // end the nested Dispatcher loop
                frame.Continue = false;
            };

            EventHandler onDispatcherInactive = (s, e) =>
            {
                operationPendingMre.Reset();

                // wait for the handle or a message
                var timeout = getTimeout();

                var nativeResult = MsgWaitForMultipleObjectsEx(
                     (uint)handles.Length, handles,
                     timeout,
                     QS_EVENTMASK,
                     MWMO_INPUTAVAILABLE);

                if (nativeResult == WAIT_OBJECT_0)
                    // handle signalled
                    result = true;
                else if (nativeResult == WAIT_TIMEOUT)
                    // timed out
                    result = false;
                else if (nativeResult == WAIT_ABANDONED_0)
                    // abandonded mutex
                    throw new AbandonedMutexException(-1, handle);
                else if (nativeResult == WAIT_OBJECT_0 + 1)
                    // operation posted from another thread, yield to the frame loop
                    return;
                else if (nativeResult == WAIT_OBJECT_0 + 2)
                {
                    // a Windows message 
                    if (getTimeout() > 0)
                    {
                        // message pending, yield to the frame loop
                        enqueIdleOperation();
                        return;
                    }

                    // timed out
                    result = false;
                }
                else
                    // unknown result
                    throw new InvalidOperationException("MsgWaitForMultipleObjectsEx");

                // end the nested Dispatcher loop
                frame.Continue = false;
            };

            dispatcher.Hooks.OperationCompleted += onOperationCompleted;
            dispatcher.Hooks.OperationPosted += onOperationPosted;
            dispatcher.Hooks.DispatcherInactive += onDispatcherInactive;

            try
            {
                // onDispatcherInactive will be called on the new frame,
                // as soon as Dispatcher becomes idle
                enqueIdleOperation();
                //using System.Windows.Threading; //From WindowsBase dll
                Dispatcher.PushFrame(frame);
            }
            finally
            {
                if (idleOperation != null)
                    idleOperation.Abort();
                dispatcher.Hooks.OperationCompleted -= onOperationCompleted;
                dispatcher.Hooks.OperationPosted -= onOperationPosted;
                dispatcher.Hooks.DispatcherInactive -= onDispatcherInactive;
            }

            return result;
        }
    }

    const uint QS_EVENTMASK = 0x1FF;
    const uint MWMO_INPUTAVAILABLE = 0x4;
    const uint WAIT_TIMEOUT = 0x102;
    const uint WAIT_OBJECT_0 = 0;
    const uint WAIT_ABANDONED_0 = 0x80;
    const uint INFINITE = 0xFFFFFFFF;

    [DllImport("user32.dll", SetLastError = true)]
    static extern uint MsgWaitForMultipleObjectsEx(
        uint nCount, IntPtr[] pHandles,
        uint dwMilliseconds, uint dwWakeMask, uint dwFlags);

    [DllImport("kernel32.dll")]
    static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
}
#endregion

References

  1. Which blocking operations cause an STA thread to pump COM messages?
  2. How to wait for WaitHandle while serving WPF Dispatcher events?
  3. What is the difference between ManualResetEvent and AutoResetEvent in .NET?
  4. C# - System.Windows.Forms.Clipboard.GetDataObject() doesnt response
  5. Timer just works if call MessageBox.Show()
  6. Synchronizing calls to the UI in a multi-threaded application
Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418