Running mousehook in background MTA thread issue

  • Thread starter Thread starter scocia888
  • Start date Start date
S

scocia888

Guest
I am running various processes in a background MTA thread with a message queue, 1 of the messages starts a global mousehook on the background thread. On clicking the mouse the UI is updated on the main thread. On starting the mousehook my application pretty much locks up the mouse so it loses its smooth movement and just jumps occasionally in the direction you are moving it. However I discovered purely by chance when putting in the message box on the background thread as shown in the code below that once the message box has appeared (and is then left alone) the mousehook works perfectly on the background thread. Problem is I can't bring up a message box as part of the app :-). Some of the code is taken from a sample which is primarily for UIA with a message queue except I have modified it to run the mouse hook on the background thread. At the moment the message queue is overkill. I expect you will want to know why I want to run a global mousehook on a background thread in the 1st place, it's a long story but basically this is working perfectly for my needs except for the message box. The problem is along the lines that the message box is keeping the background thread open to process the mouse clicks so there has to be a proper way of doing this.

As I said previously the message queue code and background MTA thread example is taken from a UIA sample and I am trying to use it to run the mousehook on a background thread. It works perfectly except the message box. Hope this all makes sense. Thank you for any help.


// Main Thread
private MouseProcessor mouseProcessor;
public delegate void MouseActionUpdateDelegate();
// Main thread dispatcher,
public Dispatcher controllerDispatcher;


mouseProcessor = new MouseProcessor();
// UpdateUI updates the user interface on the main thread

//after the background thread has done the processing
mouseProcessor.Initialize(new MouseActionUpdateDelegate(UpdateUI), controllerDispatcher);
mouseProcessor.InformBackgroundThreadToStartMouseHook();


//Mouse Processor Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Threading;

namespace MyApp
{
class MouseProcessor
{
private Dispatcher _controllerDispatcher; // Dispatcher.BeginInvoke to notify the main UI thread



private Thread _threadBackground; // Background MTA thread on which all calls to UIA are made.
private AutoResetEvent _autoEventMsg; // Event used to notify the background thread that action is required.
private AutoResetEvent _autoEventInit; // Event used to allow background thread to take any required initialization action.

// SampleMsgType and SampleMsgData are used to allow the main UI thread to communicate that specific action is required by the background thread.
private enum SampleMsgType
{
msgNull,
msgStartMouseHook,
msgCloseDown
};

private struct SampleMsgData
{
public SampleMsgType msgType;

};

// doesn't really need to support a queue of messages for action required by the
// background thread, but use a queue anyway in case requirements change in the future.
private Queue<SampleMsgData> _msgQueue = new Queue<SampleMsgData>();

private MyApp.MouseActionUpdateDelegate _UpdateUIOnMouseActionDelegate;

public void Initialize(MyApp.MouseActionUpdateDelegate updateUIOnMouseActionDelegate,
Dispatcher controllerDispatcher)
{
_UpdateUIOnMouseActionDelegate = updateUIOnMouseActionDelegate;
_controllerDispatcher = controllerDispatcher;
// The object will call UIA on a background MTA thread. This app doesn't
// expect to enter here with a background thread already initialized.
if (_threadBackground != null)
{
return;
}

// The background thread will wait for notifications when action is required.
_autoEventMsg = new AutoResetEvent(false);

// Create the background thread, and wait until it's ready to start working.
_autoEventInit = new AutoResetEvent(false);
ParameterizedThreadStart paramThreadStart = new ParameterizedThreadStart(s_DoWork);

_threadBackground = new Thread(paramThreadStart);
_threadBackground.SetApartmentState(ApartmentState.MTA);
_threadBackground.Start(this);

_autoEventInit.WaitOne();
}

public void Uninitialize()
{
// Tell the background thread to close down.
if (_threadBackground != null)
{
SampleMsgData msgData = new SampleMsgData();
msgData.msgType = SampleMsgType.msgCloseDown;
AddMsgToQueue(msgData);
}
}

private void AddMsgToQueue(SampleMsgData msgData)
{
// Request the lock, and block until it is obtained.
Monitor.Enter(_msgQueue);
try
{
// When the lock is obtained, add an element.
_msgQueue.Enqueue(msgData);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(_msgQueue);
}

// Let the background thread know there's some action to be taken.
_autoEventMsg.Set();
}

private static void s_DoWork(object data)
{
MouseProcessor mouseProcessor = (MouseProcessor)data;
mouseProcessor.ThreadProc();
}

private void ThreadProc()
{
// *** Note: The thread on which the UIA calls are made below must be MTA.
// Let the main thread know this thread is ready to start processing.

_autoEventInit.Set();

// fCloseDown will be set true when the thread is to close down.
bool fCloseDown = false;

while (!fCloseDown)
{
// Wait here unt(il we're told we have some work to do.
_autoEventMsg.WaitOne();

while (true)
{
SampleMsgData msgData;

// Note that none of the queue or message related action here is specific to UIA.
// Rather it is only a means for the main UI thread and the background MTA thread
// to communicate.

// Get a message from the queue of action-related messages.
Monitor.Enter(_msgQueue);
try
{
// An exception is thrown when the queue is empty.
msgData = _msgQueue.Dequeue();
}
catch (InvalidOperationException)
{
// InvalidOperationException is thrown if the queue is empty.
msgData.msgType = SampleMsgType.msgNull;


break;
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(_msgQueue);
}
// Process the request depending on message type coming from the queue
switch (msgData.msgType)
{

case SampleMsgType.msgStartMouseHook:
{
InitiateMouseHook();
break;
}
case SampleMsgType.msgCloseDown:
{
// The main UI thread is telling this background thread to close down.
fCloseDown = true;
break;
}

}
}
}
}

public void InformBackgroundThreadToStartMouseHook()
{


SampleMsgData msgData = new SampleMsgData();
msgData.msgType = SampleMsgType.msgStartMouseHook;
AddMsgToQueue(msgData);

}

private void InitiateMouseHook()
{


// Set up global mouse hooks to react to mouse clicks
try
{
MouseHook.MouseAction += new EventHandler(MouseHook_MouseAction);
MouseHook.Start();

//Without calling the message box below the mousehook doesn't work as described above. With the message box displayed it works perfectly as described above.

System.Windows.MessageBox.Show("","Testing", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}
catch (Exception exc)
{
System.Windows.MessageBox.Show("PROBLEM", "Testing", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}

}



private void MouseHook_MouseAction(object sender, EventArgs e)
{
_controllerDispatcher.BeginInvoke(_UpdateUIOnMouseActionDelegate);
}



}
}




"

Continue reading...
 
Back
Top