Nice, it even works outside max’s window Sometimes it stops working but the idea is great!
I’m suddenly flooded in work so somewhere next week I’ll hope to find the time to play around a bit more…
Added max restriction (optional) and mousewheel event.
For some reason though max hangs for a few seconds every time I close a window… does anyone know why?
(
str="using System;
"
str+="using System.Collections.Generic;
"
str+="using System.Text;
"
str+="using System.Runtime.InteropServices;
"
str+="using System.Windows.Forms;
"
str+="using System.Diagnostics;
"
str+="using System.Drawing;
"
str+="class InterceptMouse
"
str+="{
"
str+=" private static IntPtr _hookID = IntPtr.Zero;
"
str+=" private IntPtr MaxHwnd;
"
str+=" private bool restrictToMax;
"
str+=" public IntPtr Handle
"
str+=" {
"
str+=" get
"
str+=" {
"
str+=" return MaxHwnd;
"
str+=" }
"
str+=" set
"
str+=" {
"
str+=" if (value!=MaxHwnd)
"
str+=" {
"
str+=" MaxHwnd=value;
"
str+=" }
"
str+=" }
"
str+=" }
"
str+=" public bool RestrictToMax
"
str+=" {
"
str+=" get
"
str+=" {
"
str+=" return restrictToMax;
"
str+=" }
"
str+=" set
"
str+=" {
"
str+=" if (value==true && MaxHwnd!=IntPtr.Zero || value==false) restrictToMax=value;
"
str+=" }
"
str+=" }
"
str+=" public delegate void MouseDownHandler(object Sender, MouseEventArgs e);
"
str+=" public event MouseDownHandler MouseDown;
"
str+=" public delegate void MouseUpHandler(object Sender, MouseEventArgs e);
"
str+=" public event MouseUpHandler MouseUp;
"
str+=" public delegate void MouseMoveHandler(object Sender, MouseEventArgs e);
"
str+=" public event MouseMoveHandler MouseMove;
"
str+=" public delegate void MouseWheelHandler(object Sender, MouseEventArgs e);
"
str+=" public event MouseWheelHandler MouseWheel;
"
str+=" public InterceptMouse()
"
str+=" {
"
str+=" _hookID = SetHook(HookCallback);
"
str+=" MaxHwnd = IntPtr.Zero;
"
str+=" restrictToMax = false;
"
str+=" }
"
str+=" public void Release()
"
str+=" {
"
str+=" UnhookWindowsHookEx(_hookID);
"
str+=" }
"
str+=" private static IntPtr SetHook(LowLevelMouseProc proc)
"
str+=" {
"
str+=" using (Process curProcess = Process.GetCurrentProcess())
"
str+=" using (ProcessModule curModule = curProcess.MainModule)
"
str+=" {
"
str+=" return SetWindowsHookEx(WH_MOUSE_LL, proc,
"
str+=" GetModuleHandle(curModule.ModuleName), 0);
"
str+=" }
"
str+=" }
"
str+=" private bool IsMaxParent()
"
str+=" {
"
str+=" return (GetAncestor(GetForegroundWindow(),3)==MaxHwnd);
"
str+=" }
"
str+=" private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
"
str+=" private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
"
str+=" {
"
str+=" if ((!restrictToMax || IsMaxParent()) && nCode >= 0)
"
str+=" {
"
str+=" MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
"
str+=" if (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
"
str+=" {
"
str+=" MouseEventArgs e = new MouseEventArgs(MouseButtons.Left,1,hookStruct.pt.x ,hookStruct.pt.y,0);
"
str+=" MouseDown(this, e);
"
str+=" }
"
str+=" else if (MouseMessages.WM_LBUTTONUP == (MouseMessages)wParam)
"
str+=" {
"
str+=" MouseEventArgs e = new MouseEventArgs(MouseButtons.Left,1,hookStruct.pt.x ,hookStruct.pt.y,0);
"
str+=" MouseUp(this, e);
"
str+=" }
"
str+=" else if (MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam)
"
str+=" {
"
str+=" MouseEventArgs e = new MouseEventArgs(MouseButtons.None,0,hookStruct.pt.x ,hookStruct.pt.y,0);
"
str+=" MouseMove(this, e);
"
str+=" }
"
str+=" else if (MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)
"
str+=" {
"
str+=" MouseEventArgs e = new MouseEventArgs(MouseButtons.Right,1,hookStruct.pt. x,hookStruct.pt.y,0);
"
str+=" MouseDown(this, e);
"
str+=" }
"
str+=" else if (MouseMessages.WM_RBUTTONUP == (MouseMessages)wParam)
"
str+=" {
"
str+=" MouseEventArgs e = new MouseEventArgs(MouseButtons.Right,1,hookStruct.pt. x,hookStruct.pt.y,0);
"
str+=" MouseUp(this, e);
"
str+=" }
"
str+=" else if (MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)
"
str+=" {
"
str+=" int delta = (int)((hookStruct.mouseData & (0xFFFF << 16)))/120;
"
str+=" MouseEventArgs e = new MouseEventArgs(MouseButtons.None,1,hookStruct.pt. x,hookStruct.pt.y,delta);
"
str+=" MouseWheel(this, e);
"
str+=" }
"
str+=" }
"
str+=" return CallNextHookEx(_hookID, nCode, wParam, lParam);
"
str+=" }
"
str+=" private const int WH_MOUSE_LL = 14;
"
str+=" private enum MouseMessages
"
str+=" {
"
str+=" WM_LBUTTONDOWN = 0x0201,
"
str+=" WM_LBUTTONUP = 0x0202,
"
str+=" WM_MOUSEMOVE = 0x0200,
"
str+=" WM_MOUSEWHEEL = 0x020A,
"
str+=" WM_RBUTTONDOWN = 0x0204,
"
str+=" WM_RBUTTONUP = 0x0205
"
str+=" }
"
str+=" [StructLayout(LayoutKind.Sequential)]
"
str+=" private struct POINT
"
str+=" {
"
str+=" public int x;
"
str+=" public int y;
"
str+=" }
"
str+=" [StructLayout(LayoutKind.Sequential)]
"
str+=" private struct MSLLHOOKSTRUCT
"
str+=" {
"
str+=" public POINT pt;
"
str+=" public uint mouseData;
"
str+=" public uint flags;
"
str+=" public uint time;
"
str+=" public IntPtr dwExtraInfo;
"
str+=" }
"
str+=" [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]
"
str+=" public static extern IntPtr GetAncestor(IntPtr hWnd, int type);
"
str+=" [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]
"
str+=" public static extern IntPtr GetForegroundWindow();
"
str+=" [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]
"
str+=" private static extern IntPtr SetWindowsHookEx(int idHook,
"
str+=" LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
"
str+=" [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]
"
str+=" [return: MarshalAs(UnmanagedType.Bool)]
"
str+=" private static extern bool UnhookWindowsHookEx(IntPtr hhk);
"
str+=" [DllImport(\"user32.dll\", CharSet = CharSet.Auto, SetLastError = true)]
"
str+=" private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
"
str+=" IntPtr wParam, IntPtr lParam);
"
str+=" [DllImport(\"kernel32.dll\", CharSet = CharSet.Auto, SetLastError = true)]
"
str+=" private static extern IntPtr GetModuleHandle(string lpModuleName);
"
str+="}
"
try(mousehook.mouseops.release())catch()
global mouseHook
struct mouseHookStr
(
mouseOps,
fn CreateWinAssembly =
(
local csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
local compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.ReferencedAssemblies.addRange #("System.dll","System.Windows.Forms.dll","System.Drawing.dll")
compilerParams.GenerateInMemory = on
local compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(str)
for er =0 to compilerResults.errors.count-1 do print (compilerResults.errors.item[er].tostring())
mouseOps = compilerResults.CompiledAssembly.CreateInstance "InterceptMouse"
mouseOps.handle = (dotNetObject "System.IntPtr" (windows.GetMaxHwnd()))
mouseOps.restrictToMax = on
),
fn mouseDown s e = print "mouse down",
fn mouseUp s e = print "mouse up",
fn mouseMove s e = print "mouse move",
fn mouseWheel s e = format "Mousewheel delta: %
" e.delta,
fn initStruct =
(
CreateWinAssembly()
dotNet.addEventHandler mouseOps "MouseDown" mouseDown
dotNet.addEventHandler mouseOps "MouseUp" mouseUp
--dotNet.addEventHandler mouseOps "MouseMove" mouseMove
dotNet.addEventHandler mouseOps "MouseWheel" mouseWheel
),
_init = initStruct()
)
mouseHook = mouseHookStr()
)
-- call mousehook.mouseops.release() to stop it
i realize now i could implement the max restriction by supplying the callback with the 3dsmax thread id instead of the null pointer.
–EDIT: That seems incorrect.
i will also try to load the callback on a seperate thread to try to avoid the hang on title bar buttons.
The cool thing about Hooks is that the hook procedure can return nonzero value to prevent the system from passing the message to the target window procedure. If you are using the Events system it’s easy to add Handled property to Event Argument to control passing the message. For example, when user Right-Clicks in viewport it causes popping-up the Quad Menu. In some situation when you want to change this behavior you can cancel passing the message to the target window (viewport) using MouseDown event.
Apparently the issue with the title bar is a known bug cause by windows themes.
Doing some test with the mouse hook code posted above, did anyone ever solve the delay while closing windows?
I did some searching and found this:
Question
I have a long delay when closing applications using hooks by clicking the x button in the titlebar. If I close the application via another event (button click) for example, that works fine.
Answer
It’s a known bug of Microsoft. It has to do with the Windows themes. If you disable the Windows themes, the problem goes away. Another choice is to have the hook code run in a secondary thread.
http://www.codeproject.com/KB/cs/globalhook.aspx
I’ll have a shot at it…