I have 2 application and contractor
the contractor
namespace EventAppShellContractor
{
[ComVisible(true)]
public interface IEventAppShell
{
bool IsRunning { get; }
void Run();
void Stop();
event EventHandler<byte[]> Scanned;
}
}
Applition 1 - the shell application
This application only communication with some device ro get results
[ComVisible(true)]
public partial class Form1 : Form, IEventAppShell
{
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)AppShellEventMessage.WM_GET_HANDLER)
{
var result = (IntPtr)Marshal.GetComInterfaceForObject(this, typeof(IEventAppShell));
m.Result = result;
return;
}
base.WndProc(ref m);
}
public event EventHandler<byte[]> Scanned;
public bool IsRunning => true;
public void Run()
{
try
{
var device = new SomeNewDeviceComm();
device.Init();
device.OnScanned += async (s, data) =>
{
Scanned?.Invoke(this, data);
};
device.Start();
}
catch (Exception ex)
{
throw;
}
}
public void Stop()
{
//Close device
device?.Close();
Application.Exit();
}
}
in the paretn application when I need to run the device I'm calling this method:
public class EventAppShellRunner {
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private IEventAppShell m_Device;
private Process m_Process
public void StartDevice(){
m_Process = new Process();
m_Process.EnableRaisingEvents= true;
m_Process.StartInfo.FileName = [EXE file of 1st applcation];
m_Process.Start();
m_Process.WaitForInputIdle();
var mainFormHandle = FindWindow(null, "Form1"); //Receive Uint not zero
IntPtr result = SendMessage(mainFormHandle, (uint)AppShellEventMessage.WM_GET_HANDLER, IntPtr.Zero, IntPtr.Zero);
//Result is Zero for some reason
m_Device= (IEventAppShell)Marshal.GetObjectForNativeVariant(result);
m_Device.Scanned += SomeScanResultHandler...
//in the 'm_Device` I get exption because result is zero
m_Device.Run();
}
public void Stop(){
m_Device?.Stop();
}
}
Is there a way to communicate directly to the 1st application
I tried named pipe and it didn't work well
If the goal is to get two applications to communicate, my approach would be to setup some type of server or message queue to send serialized messages between the applications.
While this might sound complicated, most of the complicated parts are handled by libraries. I managed to setup a simple message transfer using MQTT in about an hour by following this guide. The important part here is that you can send messages, and use standard serialization libraries, like json or protobuf, to generate messages from objects. Many Lower level techniques only provide an "stream of bytes", so you need to handle things like message framing yourself.
Note that I'm not advocating for MQTT specifically, it is just one library I have used. There are many to chose from, rabbitMQ, NetMQ, MQTT, http, gRPC, and many more.
Simple and universal - use TCP socket (localhost).
Related
I am trying to subscribe to windows message events/messaging system from c# net core through unamanged c++ dll using pinvoke.
Issues I am having.
Getting the handle for my process or creating an empty window (does .net even support that).
var hwnd = Process.GetCurrentProcess().Handle;
var hwnd1 = Process.GetCurrentProcess().Handle.ToPointer();
Is either of that is valid to get the handle.
How do I marshal that handle to c++ HWND type. IntPtr seems like obvious choice, but it does not work.
Here is what I am using to subscribe to events
public class MsgSubscribe : IDisposable
{
private readonly Importer _importer;
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate Status DMsgSubscribe(uint msgType, uint msgQ, int hwnd, uint msgId);
private static DMsgSubscribe _dMsgSubscribe;
private IntPtr PMsgSubscribe { get; set; }
public bool Available { get; set; }
public MsgSubscribe(Importer importer)
{
_importer = importer;
if (_importer.hCurModule != IntPtr.Zero)
{
PMsgSubscribe = Importer.GetProcAddress(_importer.hCurModule, "MsgSubscribe");
Available = PUlyMsgSubscribe != IntPtr.Zero;
}
}
public Status MsgSubscribe(uint msgType, uint msgQ, int hwnd, uint msgId)
{
Status result = Status.FunctionNotAvailable;
if (Available)
{
_dMsgSubscribe = (DMsgSubscribe)Marshal.GetDelegateForFunctionPointer(PMsgSubscribe, typeof(DMsgSubscribe));
result = _dMsgSubscribe(msgType, msgQ, hwnd, msgId);
}
return result;
}
public void Dispose()
{
}
}
I've tried IntPtr and int for HWND marshalling, neither works.
Also I am not sure how I am supposed to catch window message based events, there is very little online if anything.
Any help appreciated.
Eventually found a way to make this work, it involves creating a window through c++ pinvoke.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Constants.Constants;
using Constants.Enums;
using Models.WindowsApiModels;
namespace Dependencies.MessagingHandling
{
public class CustomWindow : IDisposable
{
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
private const int ErrorClassAlreadyExists = 1410;
public IntPtr Handle { get; private set; }
public List<YourType> Messages { get; set; }
public void Dispose()
{
if (Handle != IntPtr.Zero)
{
Importer.DestroyWindow(Handle);
Handle = IntPtr.Zero;
}
}
public CustomWindow()
{
Messages = new List<YourType>();
var className = "Prototype Messaging Class";
WndProc mWndProcDelegate = CustomWndProc;
// Create WNDCLASS
WNDCLASS windClass = new WNDCLASS
{
lpszClassName = className,
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(mWndProcDelegate)
};
UInt16 classAtom = Importer.RegisterClassW(ref windClass);
int lastError = Marshal.GetLastWin32Error();
if (classAtom == 0 && lastError != ErrorClassAlreadyExists)
{
throw new Exception("Could not register window class");
}
// Create window
Handle = Importer.CreateWindowExW(
0,
className,
"Prototype Messaging Window",
0, 0, 0, 0, 0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero
);
}
private IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
//handle your message here
return Importer.DefWindowProc(hWnd, msg, wParam, lParam);
}
public Task GetMessage()
{
IntPtr handle = Handle;
int bRet;
while ((bRet = Importer.GetMessage(out var msg, Handle, 0, 0)) != 0)
{
switch (bRet)
{
case -1:
Console.WriteLine("Error");
CancellationToken token = new CancellationToken(true);
return Task.FromCanceled(token);
default:
Importer.TranslateMessage(ref msg);
Importer.DispatchMessage(ref msg);
break;
}
}
return Task.FromResult(true);
}
}
}
Run this in your main Method in the main thread and your menu/gui in secondary thread
Task.Run(ShowMenu);
_customWindow.GetMessage();
Importer is custom class containing c++ marshalled functions to create/handle window, look the up by the name as they are the same. All CAPS class/struct are windows/c++ api structs, those can be found on official msdn.
In general using an IntPtr is corrent.
Handle()
Returns such a IntPtr.
It looks like that you're trying to use your process HWND to get window messages which will not work because you have to use a window HWND to get the messages associated to that HWND.
I have set a viewer which is the current process and want to recive a message WM_DRAWCLIPBOARD.
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
var result = WinapiClipboard.SetClipboardViewer(Process.GetCurrentProcess().Handle);
It says right here that i should use an-application defined function to parse that message. But it never hits the function/method.
private static IntPtr WndProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam)
{
var hdc = Process.GetCurrentProcess().Handle;
var clipboard = new WinClipboard();
switch (message)
{
case WinapiClipboard.WM_DRAWCLIPBOARD:
var result = clipboard.GetUnicodeTextAsync().Result;
return IntPtr.Zero;
default:
break;
}
return WinapiClipboard.DefWindowProc(hWnd, message, wParam, lParam);
}
How i should get this message? Am i even subscribing correctly?
UPDATE:
I'm not using WinForms/WPF or any of those .NET classic framework features. All i have is .net standard 2.0 or .net core.
This is all pretty straight foward from a winforms app
Note : you cant do this from a console app (with any ease)
Declare this
[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
In your form constructor
// this.handle is your forms handle
_ClipboardViewerNext = SetClipboardViewer(this.Handle);
You will then receive the WM_DRAWCLIPBOARD messages in your form by overriding WndProc
In your form
protected override void WndProc(ref Message m)
{
switch ((Win32.Msgs)m.Msg)
{
case Win32.Msgs.WM_DRAWCLIPBOARD:
// Handle clipboard changed
break;
// ...
}
// we call this so we to pass the message along
base.WndProc(ref m);
}
Another way to do this and maybe a more modern approach
Declare this
public const int WM_CLIPBOARDUPDATE = 0x031D;
public static IntPtr HWND_MESSAGE = new IntPtr(-3);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AddClipboardFormatListener(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
In your form constructor
SetParent(Handle, HWND_MESSAGE);
AddClipboardFormatListener(Handle);
In your form
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CLIPBOARDUPDATE)
{
// handle message
}
base.WndProc(ref m);
}
if you are using a class library you'll have to create an hidden from and pass back an action or event
private class HiddenForm : Form
{
public HiddenForm()
{
SetParent(Handle, HWND_MESSAGE);
AddClipboardFormatListener(Handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CLIPBOARDUPDATE)
{
// do stuff here like call event
}
base.WndProc(ref m);
}
}
Note : None of this is tested however should get you started
Update 1
SetClipboardViewer expects a window handle, not a process handle. If
you don't have a window handle, create a message-only window for the
sole purpose of receiving clipboard messages - thanks to IInspectable
Update 2
This will only ever work on the kind of machine that also always has
the full framework available. - thanks to Hans Passant
Since you are doing a winforms project override the existing Control.WndProc rather than provide a static.
protected override void WndProc(ref Message m)
{
if(WM_DRAWCLIPBOARD == msg.Msg)
{ ... }
else
base.WndProc(ref msg);
}
I am working on an WPF application to monitor my activities on my computer. I use Process.GetProcesses() and some filtering to get the processes I am interested in (example:Calculator) then I record their StartTime. I am also using WIN32/USER32 API method GetForegroundWindow() to get the window the user is using.
The problem is that when the windows are Windows/UWP applications they are always hosted by the process ApplicationFrameHost. So the GetForegroundWindow() method returns that window with a title (example:Calculator), but not the real process being hosted.
What I need is either another way to get the foreground window that includes the real process being hosted, or some way to connect the window to process.
Anyone that knows how to accomplish this? All help would be really appreciated.
I eventually found a way to do this, so I am going answer my own question so maybe someone in the future with the same problem could find it useful.
This is the class with the WinApiFunctions:
public class WinAPIFunctions
{
//Used to get Handle for Foreground Window
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetForegroundWindow();
//Used to get ID of any Window
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
public delegate bool WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc callback, IntPtr lParam);
public static int GetWindowProcessId(IntPtr hwnd)
{
int pid;
GetWindowThreadProcessId(hwnd, out pid);
return pid;
}
public static IntPtr GetforegroundWindow()
{
return GetForegroundWindow();
}
}
And this is the class I used to test if it would work. I used it in a simple console program that just writes out the name of the process that has current focus:
class FindHostedProcess
{
public Timer MyTimer { get; set; }
private Process _realProcess;
public FindHostedProcess()
{
MyTimer = new Timer(TimerCallback, null, 0, 1000);
Console.ReadKey();
}
private void TimerCallback(object state)
{
var foregroundProcess = Process.GetProcessById(WinAPIFunctions.GetWindowProcessId(WinAPIFunctions.GetforegroundWindow()));
if (foregroundProcess.ProcessName == "ApplicationFrameHost")
{
foregroundProcess = GetRealProcess(foregroundProcess);
}
Console.WriteLine(foregroundProcess.ProcessName);
}
private Process GetRealProcess(Process foregroundProcess)
{
WinAPIFunctions.EnumChildWindows(foregroundProcess.MainWindowHandle, ChildWindowCallback, IntPtr.Zero);
return _realProcess;
}
private bool ChildWindowCallback(IntPtr hwnd, IntPtr lparam)
{
var process = Process.GetProcessById(WinAPIFunctions.GetWindowProcessId(hwnd));
if (process.ProcessName != "ApplicationFrameHost")
{
_realProcess = process;
}
return true;
}
}
Chris, there is the alternative way which I have discovered trying to apply your solution to a related problem. While trying to analyse how the ApplicationFrameHost.exe-related stuff worked, I have stumbled upon the documented way of getting the true foreground window / thread and its process by passing 0 instead of the actual thread ID to GetGUIThreadInfo.
It might not fully work for the edge-case scenarios of your problem, but I felt that this might be a useful contribution for people of the future who might face the same problems ;-)
Here is the example of how this can be applied (pseudo C++'ish code):
GUITHREADINFO gti = { sizeof(GUITHREADINFO) };
GetGUIThreadInfo(0, >i); // <- note the `0`
DWORD processId = 0;
GetWindowThreadProcessId(gti.hwndFocus, &processId);
const auto procName = Util::GetProcessName(processId);
It solved my problem (obtaining the actual keyboard layout + finding the real foreground window) for all more-or-less common apps I have tested it against.
Basically, the answer you provided will also fail if the active window is in full screen mode,
for example if you have Skype app opened along with Microsoft Remote Desktop, which is the
active one and on full screen mode, EnumChildWindows would return SkypeApp not RDPClient.
and to get this fixed, you should follow the workaround suggested by Ivanmoskalev,
check this out:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);
public static IntPtr getThreadWindowHandle(uint dwThreadId)
{
IntPtr hWnd;
// Get Window Handle and title from Thread
var guiThreadInfo = new GUITHREADINFO();
guiThreadInfo.cbSize = Marshal.SizeOf(guiThreadInfo);
GetGUIThreadInfo(dwThreadId, ref guiThreadInfo);
hWnd = guiThreadInfo.hwndFocus;
//some times while changing the focus between different windows, it returns Zero so we would return the Active window in that case
if (hWnd == IntPtr.Zero)
{
hWnd = guiThreadInfo.hwndActive;
}
return hWnd;
}
[DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr windowHandle, out int processId);
static void Main(string[] args)
{
var current = getThreadWindowHandle(0);
int processId = 0;
GetWindowThreadProcessId(current, out processId);
var foregroundProcess = GetActiveProcess(processId);
}
private static Process GetActiveProcess(int activeWindowProcessId)
{
Process foregroundProcess = null;
try
{
foregroundProcess = Process.GetProcessById(activeWindowProcessId);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
if (string.IsNullOrWhiteSpace(GetProcessNameSafe(foregroundProcess)))
{
var msg = "Process name is empty.";
Console.WriteLine(msg);
}
return foregroundProcess;
}
About the same code from Chris Johnsson except that I'm not using Process because it doesn't work very well on the UWP app.
Code for start:
new Timer(TimerCallback, null, 0, 1000);
void TimerCallback(object state)
{
var process = new ProcessUtils.FindHostedProcess().Process;
string name = string.Empty;
if (process.IsPackaged)
{
var apps = process.GetAppDiagnosticInfos();
if (apps.Count > 0)
name = apps.First().AppInfo.DisplayInfo.DisplayName;
else
name = System.IO.Path.GetFileNameWithoutExtension(process.ExecutableFileName);
}
else
name = System.IO.Path.GetFileNameWithoutExtension(process.ExecutableFileName);
Debug.WriteLine(name);
}
And the FindHostedProcess class where I use ProcessDiagnosticInfo instead of Process
public class FindHostedProcess
{
public ProcessDiagnosticInfo Process { get; private set; }
public FindHostedProcess()
{
var foregroundProcessID = WinAPIFunctions.GetforegroundWindow();
Process = ProcessDiagnosticInfo.TryGetForProcessId((uint)WinAPIFunctions.GetWindowProcessId(foregroundProcessID));
// Get real process
if (Process.ExecutableFileName == "ApplicationFrameHost.exe")
WinAPIFunctions.EnumChildWindows(foregroundProcessID, ChildWindowCallback, IntPtr.Zero);
}
private bool ChildWindowCallback(IntPtr hwnd, IntPtr lparam)
{
var process = ProcessDiagnosticInfo.TryGetForProcessId((uint)WinAPIFunctions.GetWindowProcessId(hwnd));
if (process.ExecutableFileName != "ApplicationFrameHost.exe")
Process = process;
return true;
}
}
Finally reuse Chris Johnson's WinAPIFunctions class.
I was looking for a possibility to be notified in a .NET windows application when any window is activated in the OS (Windows XP 32-bit). On CodeProject I have found a solution by using global system hooks.
http://www.codeproject.com/Articles/18638/Using-Window-Messages-to-Implement-Global-System-H .
Here is a short summary of this procedure:
In an unmanaged assembly (written in C++) a method is implemented which installs the WH_CBT hook.
bool InitializeCbtHook(int threadID, HWND destination)
{
if (g_appInstance == NULL)
{
return false;
}
if (GetProp(GetDesktopWindow(), " HOOK_HWND_CBT") != NULL)
{
SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "HOOK_HWND_CBT"),
RegisterWindowMessage("HOOK_CBT_REPLACED"), 0, 0);
}
SetProp(GetDesktopWindow(), " HOOK_HWND_CBT", destination);
hookCbt = SetWindowsHookEx(WH_CBT, (HOOKPROC)CbtHookCallback, g_appInstance, threadID);
return hookCbt != NULL;
}
In the callback method (filter function) depending on the hook type windows messages are sent to a destination window.
static LRESULT CALLBACK CbtHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code >= 0)
{
UINT msg = 0;
if (code == HCBT_ACTIVATE)
msg = RegisterWindowMessage("HOOK_HCBT_ACTIVATE");
else if (code == HCBT_CREATEWND)
msg = RegisterWindowMessage("HOOK_HCBT_CREATEWND");
else if (code == HCBT_DESTROYWND)
msg = RegisterWindowMessage("HOOK_HCBT_DESTROYWND");
else if (code == HCBT_MINMAX)
msg = RegisterWindowMessage("HOOK_HCBT_MINMAX");
else if (code == HCBT_MOVESIZE)
msg = RegisterWindowMessage("HOOK_HCBT_MOVESIZE");
else if (code == HCBT_SETFOCUS)
msg = RegisterWindowMessage("HOOK_HCBT_SETFOCUS");
else if (code == HCBT_SYSCOMMAND)
msg = RegisterWindowMessage("HOOK_HCBT_SYSCOMMAND");
HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), HOOK_HWND_CBT");
if (msg != 0)
SendNotifyMessage(dstWnd, msg, wparam, lparam);
}
return CallNextHookEx(hookCbt, code, wparam, lparam);
}
To use this assembly in a .NET Windows Application the following method has to be imported:
[DllImport("GlobalCbtHook.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitializeCbtHook (int threadID, IntPtr DestWindow);
[DllImport("GlobalCbtHook.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void UninitializeCbtHook(int hookType);
After calling InitializeCbtHook the messages received from GlobalCbtHook.dll can be processed in:
protected override void WndProc(ref Message msg)
The messages have to be registered in both the assembly and the application by calling
RegisterWindowMessage.
[DllImport("user32.dll")]
private static extern int RegisterWindowMessage(string lpString);
This implementation works fine. But in most cases when I activate Microsoft Office Outlook
my .NET Application receives the activate-event after I minimize Outlook or activate an other window. At first I thought that my .NET wrapper is the cause of the problem. But after I used the sources from the above link I could recognized the same behaviour.
My actually workaround is to use WH_SHELL hook. I know that one difference between WH_CBT and WH_SHELL hook is when using WH_CBT hook it is possible to interrupt the filter function chain by not calling the CallNextHookEx method. Could this play a role in my problem?
Please provide help.
obviously the hooking does not work in cases of outlook - what about other microsoft products (word, power point ...)??
but, why hooking? this little class will work even if outlook is activated
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsMonitor
{
public class ActiveWindowChangedEventArgs : EventArgs
{
public IntPtr CurrentActiveWindow { get; private set; }
public IntPtr LastActiveWindow { get; private set; }
public ActiveWindowChangedEventArgs(IntPtr lastActiveWindow, IntPtr currentActiveWindow)
{
this.LastActiveWindow = lastActiveWindow;
this.CurrentActiveWindow = currentActiveWindow;
}
}
public delegate void ActiveWindowChangedEventHandler(object sender, ActiveWindowChangedEventArgs e);
public class ActiveWindowMonitor
{
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
private Timer monitorTimer;
public IntPtr ActiveWindow { get; private set; }
public event ActiveWindowChangedEventHandler ActiveWindowChanged;
public ActiveWindowMonitor()
{
this.monitorTimer = new Timer();
this.monitorTimer.Tick += new EventHandler(monitorTimer_Tick);
this.monitorTimer.Interval = 10;
this.monitorTimer.Start();
}
private void monitorTimer_Tick(object sender, EventArgs e)
{
CheckActiveWindow();
}
private void CheckActiveWindow()
{
IntPtr currentActiveWindow = GetForegroundWindow();
if (this.ActiveWindow != currentActiveWindow)
{
IntPtr lastActiveWindow = this.ActiveWindow;
this.ActiveWindow = currentActiveWindow;
OnActiveWindowChanged(lastActiveWindow, this.ActiveWindow);
}
}
protected virtual void OnActiveWindowChanged(IntPtr lastActiveWindow, IntPtr currentActiveWindow)
{
ActiveWindowChangedEventHandler temp = ActiveWindowChanged;
if (temp != null)
{
temp.Invoke(this, new ActiveWindowChangedEventArgs(lastActiveWindow, currentActiveWindow));
}
}
}
}
usage
public void InitActiveWindowMonitor()
{
WindowsMonitor.ActiveWindowMonitor monitor = new WindowsMonitor.ActiveWindowMonitor();
monitor.ActiveWindowChanged += new WindowsMonitor.ActiveWindowChangedEventHandler(monitor_ActiveWindowChanged);
}
private void monitor_ActiveWindowChanged(object sender, WindowsMonitor.ActiveWindowChangedEventArgs e)
{
//ouh a window got activated
}
I want to leverage machine learning to model a user's intent and potentially automate commonly performed tasks. To do this I would like to have access to a fire-hose of information about user actions and the machine state. To this end, it is my current thinking that getting access to the stream of windows messages is probably the way forward.
I would like to have as much information as is possible, filtering the information to that which is pertinent I would like to leave to the machine learning tool.
How would this be accomplished? (Preferably in C#).
Please assume that I know how to manage and use this large influx of data.
Any help would be gratefully appreciated.
You can use SetWindowsHookEx to set low level hooks to catch (specific) windows messages.
Specifically these hook-ids might be interesting for monitoring:
WH_CALLWNDPROC (4) Installs a hook procedure that monitors messages
before the system sends them to the destination window procedure. For
more information, see the CallWndProc hook procedure.
WH_CALLWNDPROCRET(12) Installs a hook procedure that monitors
messages after they have been processed by the destination window
procedure. For more information, see the CallWndRetProc hook
procedure.
It's been a while since I've implemented it, but as an example I've posted the base class I use to hook specific messages. (For example, I've used it in a global mousewheel trapper, that makes sure my winforms apps behave the same as internet explorer: scroll the control underneath the cursor, instead of the active control).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Subro.Win32;
namespace Subro
{
/// <summary>
/// Base class to relatively safely register global windows hooks
/// </summary>
public abstract class GlobalHookTrapper : FinalizerBase
{
[DllImport("user32", EntryPoint = "SetWindowsHookExA")]
static extern IntPtr SetWindowsHookEx(int idHook, Delegate lpfn, IntPtr hmod, IntPtr dwThreadId);
[DllImport("user32", EntryPoint = "UnhookWindowsHookEx")]
private static extern int UnhookWindowsHookEx(IntPtr hHook);
[DllImport("user32", EntryPoint = "CallNextHookEx")]
static extern int CallNextHook(IntPtr hHook, int ncode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThreadId();
IntPtr hook;
public readonly int HookId;
public readonly GlobalHookTypes HookType;
public GlobalHookTrapper(GlobalHookTypes Type):this(Type,false)
{
}
public GlobalHookTrapper(GlobalHookTypes Type, bool OnThread)
{
this.HookType = Type;
this.HookId = (int)Type;
del = ProcessMessage;
if (OnThread)
hook = SetWindowsHookEx(HookId, del, IntPtr.Zero, GetCurrentThreadId());
else
{
var hmod = IntPtr.Zero; // Marshal.GetHINSTANCE(GetType().Module);
hook = SetWindowsHookEx(HookId, del, hmod, IntPtr.Zero);
}
if (hook == IntPtr.Zero)
{
int err = Marshal.GetLastWin32Error();
if (err != 0)
OnHookFailed(err);
}
}
protected virtual void OnHookFailed(int Error)
{
throw Win32Functions.TranslateError(Error);
}
private const int HC_ACTION = 0;
[MarshalAs(UnmanagedType.FunctionPtr)]
private MessageDelegate del;
private delegate int MessageDelegate(int code, IntPtr wparam, IntPtr lparam);
private int ProcessMessage(int hookcode, IntPtr wparam, IntPtr lparam)
{
if (HC_ACTION == hookcode)
{
try
{
if (Handle(wparam, lparam)) return 1;
}
catch { }
}
return CallNextHook(hook, hookcode, wparam, lparam);
}
protected abstract bool Handle(IntPtr wparam, IntPtr lparam);
protected override sealed void OnDispose()
{
UnhookWindowsHookEx(hook);
AfterDispose();
}
protected virtual void AfterDispose()
{
}
}
public enum GlobalHookTypes
{
BeforeWindow = 4, //WH_CALLWNDPROC
AfterWindow = 12, //WH_CALLWNDPROCRET
KeyBoard = 2, //WH_KEYBOARD
KeyBoard_Global = 13, //WH_KEYBOARD_LL
Mouse = 7, //WH_MOUSE
Mouse_Global = 14, //WH_MOUSE_LL
JournalRecord = 0, //WH_JOURNALRECORD
JournalPlayback = 1, //WH_JOURNALPLAYBACK
ForeGroundIdle = 11, //WH_FOREGROUNDIDLE
SystemMessages = 6, //WH_SYSMSGFILTER
MessageQueue = 3, //WH_GETMESSAGE
ComputerBasedTraining = 5, //WH_CBT
Hardware = 8, //WH_HARDWARE
Debug = 9, //WH_DEBUG
Shell = 10, //WH_SHELL
}
public abstract class FinalizerBase : IDisposable
{
protected readonly AppDomain domain;
public FinalizerBase()
{
System.Windows.Forms.Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
domain = AppDomain.CurrentDomain;
domain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
domain.DomainUnload += new EventHandler(domain_DomainUnload);
}
private bool disposed;
public bool IsDisposed{get{return disposed;}}
public void Dispose()
{
if (!disposed)
{
GC.SuppressFinalize(this);
if (domain != null)
{
domain.ProcessExit -= new EventHandler(CurrentDomain_ProcessExit);
domain.DomainUnload -= new EventHandler(domain_DomainUnload);
System.Windows.Forms.Application.ApplicationExit -= new EventHandler(Application_ApplicationExit);
}
disposed = true;
OnDispose();
}
}
void Application_ApplicationExit(object sender, EventArgs e)
{
Dispose();
}
void domain_DomainUnload(object sender, EventArgs e)
{
Dispose();
}
void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
Dispose();
}
protected abstract void OnDispose();
/// Destructor
~FinalizerBase()
{
Dispose();
}
}
}