Sorry, but you are not on the right track, not even close.
You can't pass a message ID to SetWindowsHookEx() like you are doing. The 1st parameter expects a hook type (one of the WH_... constants), not a message ID. To hook window messages, you need to specify either the WH_GETMESSAGE or WH_CALLWNDPROC/RET hook type, and then look for your desired message ID inside of your hook callback function.
However, in this particular situation, CM_VISIBLECHANGED is a private message to the VCL only, it does not go through the Win32 messaging system at all, so a SetWindowsHookEx() hook will never see it. The only way you can intercept CM_VISIBLECHANGED is to either:
subclass the WindowProc property of every TControl you are interested in.
derive your own UI classes that either override the virtual WndProc() or DefaultHandler() method, or define a 'message' handler for CM_VISIBECHANGED.
use a detouring library, such as MSDetours, to hook into the non-virtual TControl.Perform() method directly (which is what the TControl.Visible property setter calls to send out CM_VISIBLECHANGED).
This won't help you in this particular situation, but in general to retrieve the relevant HWND in a SetWindowsHookEx() hook:
for WH_GETMESSAGE, the lParam is a pointer to a MSG structure, which has an HWND member.
for WH_CALLWNDPROC, the lParam is a pointer to a CWPSTRUCT structure, which has an HWND member.
for WH_CALLWNDPROCRET, the lParam is a pointer to a CWPRETSTRUCT structure, which has an HWND member.