0

I have a low level mouse hook which I am attempting to use to handle global left click events. I have defined the hook in a base main form class like so:

class TMainForm : public TForm
{
private:
    HHOOK hMouseHook;
    static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
    void __fastcall MouseHook(int nCode, WPARAM wParam, LPARAM lParam);
};
extern PACKAGE TMainForm *MainForm;


LRESULT CALLBACK TMainForm::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    MainForm->MouseHook(nCode, wParam, lParam);
    return CallNextHookEx(0, nCode, wParam, lParam);
}

void TMainForm::MouseHook(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
    {

        HWND__ * handle = Platform::Win::WindowHandleToPlatform(this->Handle)->Wnd;

        RECT Rect;
        GetWindowRect(handle, &Rect);
    }
}

My mouse hook detects a left click just fine, however when I attempt to get the handle of my MainForm I get an access violation.

I originally thought this could be because I was trying to retrieve the handle in the TMainForm class, when in reality during run time, a form which inherits from TMainForm would be active. So to do this, I wrote a virtual method called GetHandle().

This function is defined in the TMainForm header like this:

virtual HWND__ * __fastcall GetHandle();

and in the inheriting class like this:

HWND__ * __fastcall TMainFormPass::GetHandle()
{
   return Platform::Win::WindowHandleToPlatform(this->Handle)->Wnd;
}

However, when I call GetHandle() in TMainForm I still get an access violation. Additionally, after trying a few more things out, I realized that calling any virtual function at all from inside of MouseHook causes an access violation.

Why am I not able to retrieve my forms handle using Platform::Win::WindowHandleToPlatform(this->Handle)->Wnd; inside of MouseHook? Why does calling a virtual function inside of MouseHook cause an access violation?

Edit:

To test Remy's proposed answer of the MainForm pointer being invalid, I added a block of code to the TMainForm constructor to test if the pointer was valid. Additionally, I added a block of code to the constructor of the class which inherits from TMainForm and is loaded when starting my application, to test its global pointer. The global MainForm pointer in my TMainForm class was in fact invalid. However, the global pointer for the inheriting class was valid, being that it was the class actually being instantiated.

James Hogle
  • 3,010
  • 3
  • 22
  • 46

1 Answers1

1

What you describe suggests that your global MainForm pointer is not pointing at a valid Form object when the hook is called. Any call to a method of the form, such as MouseHook(), will thus have an invalid this pointer. You must make sure you are assigning that pointer.

You are using the SetWindowsHookEx() example that I gave you in answer to another question. In that same answer, I had also given you an alternative solution that did not involve SetWindowsHookEx() - handling the WM_INPUT message from the RAW Input API. Your question did not mention that you are using FireMonkey instead of VCL. The example I had given you was for VCL. I have now updated that answer to include a FireMonkey example as well.

Community
  • 1
  • 1
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • @RemyLebau I appreciate you going out of your way with editing your answer to my other question! I will definitely look into using RawInput. I just wanted to see if I could learn what was going wrong in this case since I have everything all typed out. In the case of the `MouseHook()` method, when you say it will have an invalid `this` pointer, is that referring to the hidden `this` pointer? – James Hogle Oct 09 '15 at 20:45
  • If the `MainForm` pointer used in the `MainForm->MouseHook(...)` call is invalid, the `this` pointer inside of `MouseHook()` will likewise be invalid, so `this->Handle` and other accesses of `this->` will fail. `MainForm` must be assigned, either by `Application->CreateForm()` at app startup, or `MainForm = new TMainForm(...)` somewhere in your code. Also, if you keep using `SetWindowsHookEx()`, call `UnhookWindowsHookEx()` when the Form is destroyed. Even then, the hook can still be called again so set `MainForm` to NULL and have `MouseHookProc()` check for that before calling `MouseHook()`. – Remy Lebeau Oct 09 '15 at 20:53