MouseStruct and AppMouseStruct

Purpose -

The purpose of this document is to have, for archival purposes, the logic behind the MouseStruct and AppMouseStruct structures that are used in the MouseKeyboardInputMonitor.WinApi namespace.

Note: The screenshots from disassembly are located here in a 7zip archive.

Section 1 – History

To begin some background history is necessary. With V2 of the project, the MouseHookStruct was defined as follows:
private struct MouseHookStruct {
    public Point Point;
    public Int32 MouseData;
    public Int32 Flags;
    public Int32 Time;
    public IntPtr ExtraInfo; }
This is consistent with MSDN Documentation on MSLLHOOKSTRUCT.
One issue quickly arose regarding application hooks. Application Hooks use MOUSEHOOKSTRUCTEX, in which the scroll data is stored in ExtraInfo, which is conveniently the same offset as in the MSLLHOOKSTRUCT. At that time this eliminated the need for two structures.
Another issue presented itself on 64-bit builds with application hooks. The data was always 0 in ExtraInfo. This post on the CodeProject article stated that for working AppHooks ExtraInfo, IntPtrs should be used to provide 64-bit compatibility. This was tested and proved a working solution. The logic is that the datatype change to QWORDS in 64-bit. In a way it does, but not how I assumed at that time. The struct was redefined at that time with IntPtrs in lieu of Int32 datatypes.
As suggested by George in Tickets #262 and #263, a more elegant approach to double-click detection should be found, other than the DateTime.UtcNow comparison approach used. Application hooks report the use of WM_DoubleClick through wParam, but not global which was why the UtcNow time comparisons were used. The idea was to use the Timestamp from MouseHookStruct.Time.
Once again, problems appeared dealing with the definition of MouseHookStruct. In 64-bit builds, the Time member was always 0. Interestingly, the timestamp was stored within the Flags member.

Section 2 – Disassembly

My first step was to obtain the memory address of the unmanaged mouse struct. This is accomplished by knowing the value of LParam. LParam is a pointer to the unmanaged memory location of the struct, and is then marshalled into managed code by Marshal.PtrToStructure(). A simple Console.WriteLine() containing LParam with the output in hex does the trick.

Debugging the test application, the value of LParam is shown in Console. Using CheatEngine attached to the debugged application, one can then open memory view to see the hex-disassembly of the unmanaged structure. Moving the mouse should then cause certain memory values to change, which highlight in red. This gives a general idea of where each member is. Additionally, other input can be used, such as XButtons or scrolling.
The next step is to add each datatype to the CheatEngines address list, in order to acquire accurate structure information.
Upon starting, my goal was to document 32-bit and 64-bit, MSLLHOOKSTRUCT and MOUSEHOOKSTRUCTEX.

Screenshots of the dissassembly


I performed the above steps in both architectures for global hooks first. My conclusion after comparing the two is that there is no architecture difference for MSLLHOOKSTRUCT, and that it has been defined correctly on MSDN. The previous redefinition of the structure for the application was wrong, because it used 8 byte datatypes in a 64-bit architecture. That is why the timestamp was located within MouseHookStruct.Flags and why the Time member was always 0.

Application Hooks and MOUSEHOOKSTRUCTEX

Moving on to application hooks, the same disassembly method was executed. There was however, one major problem. I could not locate the unmanaged MOUSEHOOKSTRUCTEX. The LParam pointed to a memory location which was completely static, and after a brief review, it is clear that it is NOT the MOUSEHOOKSTRUCTEX. I attempted in both x86 and x84 to no avail, and multiple times each. A CheatEngine memory search did not yield any result either (except for the memory location of the labelMousePoint.Text)

The only mouse information truly necessary for application hooks is the mouse location and the extraInfo, or more specifically the WORD containing the mouseDelta and Xbutton information. It is already known that Point is correct as the first member of the structure. And a working architecture-independent structure is already known, using IntPtrs was being used.

So I guessed the offset of the ExtraInfo by the adding the datatype sizes up from the misdefined struct.
32-bit 64-bit
Point (8 Byte) Point (8 Byte)
MouseData (4 Byte) MouseData (8 Byte)
Flags (4 Byte) Flags (8 Byte)
+ Time (4 Byte) + Time (8 Byte)
=========== ===========
0x14 – ExtraInfo 0x20 – ExtraInfo

So the offset for ExtraInfo is 0x14 in 32-bit and 0x20 in 64-bit. I am not concerned with the other members of MOUSEHOOKSTRUCTEX or their respective offsets.

Section 3 – C# Structures

There is really very little information that is needed for the mouse hooks. The Point location, Timestamp, and MouseData. Instead of using a sequential struct layout and filling up a structure with information that is not needed, like Flags and ExtraInfo, I opted to create two different structs with explicit offsets to simplify the HookProc code as much as possible. They are MouseStruct and AppMouseStruct, which are defined here with further description below.

Final MouseStruct Definition

private struct MouseStruct {
    public Point Point;
    public Int16 MouseData; 
    public Int32 Time; }

Final AppMouseStruct Definition

private struct AppMouseStruct {
    public Point Point;
#if IS_X64
    public Int16 MouseData; }

It also occurred to me that with explicit offsets, there is no need to have MouseData declared as an Int32 and then later retrieve the high-order WORD, but rather to use an Int16 in the structure and simplify the entire process of mouse delta and x-button determination. To do this, the explicit offset of MouseData must be increased by 0x02 to ignore the low-order word.

While it has not been mentioned yet, it is important to know that the MOUSEHOOKSTRUCTEX does not have a timestamp. Consequently, the AppMouseStruct does not have a timestamp. The primary purpose of the AppMouseStruct is to retrieve the Point and MouseData and pass the it on to a MouseStruct via the ToMouseStruct() function. Without a timestamp, this cannot be done with 100% accuracy and a timestamp is generated at the time of that call. This timestamp is then used in the MouseEventExtArgs only; the application hook does not require a perfectly accurate timestamp for DoubleClick determination, which was the original issue with global hooks in the first place.

Section 4 – My Closing Remarks

The Windows API is crazy, and if Microsoft ever asks me I will advise a complete rewrite of the Kernel, FileSystem, and Operating System, and for everyone’s sake, open source it. The likelihood of any of that actually occurring is astronomical.

Until then, we’re left with trying to figure out why a thing like the MSLLHOOKSTRUCT is architecture independent and MOUSEHOOKSTRUCTEX is not, and how to use it in managed code. I hope I have done enough for the Mouse and Keyboard Hooks project in this aspect.

Last edited Oct 9, 2011 at 11:10 AM by PyrrhicVictor, version 5