Results 1 to 26 of 26

Thread: I'm Back!! Global Hook problem

  1. #1

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037

    I'm Back!! Global Hook problem

    I'ts been a long time. How are you CornedBee?

    I have been programming in C# for a while now and now am forced to make a C++ dll to set up a global keyboard hook for a clipboard program I am making.

    My code never enters DllMain!! Is that ok?
    Code:
    #include <windows.h>
    #include "Global Hook.h"
    #include <stdio.h>
    
    #pragma data_seg(".shared")
    #pragma data_seg()
    
    //extern "C" tells compiler not to change the name of the 
    //function so that it can be called directly from another program.
    extern "C" _declspec(dllexport) HHOOK SetHook();
    extern "C" _declspec(dllexport) void Unhook();
    _declspec(dllexport) int _WEP (int bSystemExit);
    
    HHOOK hKeyhook=0;
    HINSTANCE hDll = LoadLibrary((LPCTSTR)"WinHook.dll");
    
    //---------------------------
    BOOL APIENTRY DllMain (
      HINSTANCE hInst     /* Library instance handle. */ ,
      DWORD reason        /* Reason this function is being called. */ ,
      LPVOID reserved     /* Not used. */ )
    {
        if ( reason == DLL_PROCESS_ATTACH)
           hDll = hInst;
    
    MessageBox(NULL, TEXT("ENTERED DLLMAIN"), TEXT(""), MB_OK);
        //return (int)(DisableThreadLibraryCalls (hDll));
    
        /* Returns TRUE on success, FALSE on failure */
        return TRUE;
    }
    
    //---------------------------
    LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
       // This is where you want to decide what to do when receiving keyboard parameters.
       MessageBox(NULL, TEXT("In CallBack"), TEXT("KeyPress"), MB_OK);
    
        return CallNextHookEx(hKeyhook, nCode, wParam, lParam);  //CallNextHookEx returns long value.
    }
    
    //---------------------------
    extern "C" _declspec(dllexport) HHOOK SetHook()
    {
        if(hKeyhook == NULL)
        {
             hKeyhook = SetWindowsHookEx(WH_KEYBOARD, KeyBoardProc, hDll, 0);
            
             if (hKeyhook == NULL)
                MessageBox(NULL, TEXT("Failed"), TEXT("Failed"), MB_OK);
             else
                MessageBox(NULL, TEXT("Hooked!!!"), TEXT("Success!!!"), MB_OK);
        }
    
        return hKeyhook;
    }
    It works but how do I notify my C# app that the callback was called and get which key was pressed?
    Last edited by aewarnick; Jun 10th, 2003 at 11:06 PM.

  2. #2
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    I'm fine, thanks. How about you?

    You get the key by evaluating wParam and lParam (see docs for how).

    Actually it's not okay that DllMain isn't called - is it a managed DLL? Maybe that's an effect of it.

    For notification: that's quite tricky because of the way managed and unmanaged code work together (or rather, against ).
    One option would be to compile the DLL as managed code. The hook procedure would be unmanaged (using some #pragma directive). The SetHook function would be a static method of a class, so that you can access it directly from C#. It would take some notification delegate as parameter and store it somewhere - the tricky thing here is breaching the process border.
    Probably the best thing would be to store the process handle of the original process of the DLL in shared memory. Then use this handle to use CreateRemoteThread to create a thread in the original process from whereever the hook procedure was called. This thread then invokes the delegate.

    Something like this:
    Code:
    // The DLL:
    
    // shared memory:
    HANDLE hProcess;
    
    // normal memory
    __delegate void KeyNotify(int keyCode);
    KeyNotify *pNotify;
    
    // Declaration
    unsigned long ManagedDo(int key);
    #pragma unmanaged
    DWORD ThreadProc(void *d);
    
    // hook proc
    LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
      int keyCode = DECODE_KEYCODE_SOMEHOW(wParam, lParam);
      CreateRemoteThread(hProcess, ThreadProc, (void*)keyCode);
    
      return CallNextHookEx(nCode, wParam, lParam);
    }
    
    // ThreadProc
    DWORD ThreadProc(void *d)
    {
      return ManagedDo((int)d);
    }
    
    // ManagedDo
    #pragma managed
    unsigned long ManagedDo(int key)
    {
      pNotify->Invoke(key);  // or something like that
    }
    
    // SetHook
    namespace You
    {
    public __sealed __gc class GlobalHook
    {
      public static System::IntPtr SetHook(KeyNotify *pNot) {
        pNotify = pNot;
        hProcess = GetProcessHandle();
        // Set the hook.
      }
    };
    };

    No guarantees at all that it might work, it's just an idea and I can't test it here.
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

  3. #3

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    I'm doing ok. I'm still learning.

    Actually it is completely unmanaged, written in pure C++ with Dev. I don't think I can write managed peices of code and unmanaged with C# anyways. With C++.net yes but I am not using that.

    Shouldn't dllmain be called first thing? I am going to need a different idea I think, in getting the callback notifications to the C# app from the unmanaged dll with the key codes (wParam lParam).

  4. #4
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    Purely unmanaged, in Dev-C++?

    That might be tricky, I have no idea how to do it.


    There was something with conversion between function pointers and delegates in managed/unmanaged bridges, maybe you'll find something in the P/Invoke docs.
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

  5. #5

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    The site I got that code from said that you would have to use a message hook to let the .net program know about a callback and the key that was pressed. But maybe that was not for .net but for unmanaged programs.

  6. #6

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    Here is the site I got that code from:
    http://www.experts-exchange.com/Prog..._20453601.html

    In the thread it says:

    "Wow, that's all brilliant! It works system wide.
    But I have one last question: Is it possible to send the keys somehow from the dll to the exe file?

    Well it is possible but it will take an additional hook. Similarly to the keyboard hook, you'll need to have a GetMessage Hook.

    //Set Message Hook
    hMsghook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hdll,0);

    // Unhook Message
    UnhookWindowsHookEx(hMsghook);

    // Message process
    LRESULT CALLBACK GetMsgProc(int nCode,WPARAM wParam,LPARAM lParam)

    In GetMsgProc, you can modify Windows Messages and send another key not only to the exe but also to any other applications."

    If that is my answer I would like help doing it.??

  7. #7
    Kitten CornedBee's Avatar
    Join Date
    Aug 2001
    Location
    In a microchip!
    Posts
    11,594
    No, that's not what you want.
    All the buzzt
    CornedBee

    "Writing specifications is like writing a novel. Writing code is like writing poetry."
    - Anonymous, published by Raymond Chen

    Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.

  8. #8
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    For a WH_KEYBOARD hook the wParam is the VKey of the key that generated the keyboard event and lParam is composed of a number of bit masks indicating the state of the keyboard when the key event occurs.

    vis.
    Bits 0-15 is an integer being the key repetition count (if the key was held down, for instance)
    Bits 16-23 is the keyboard scancode of the key - this is hardware dependent.
    Bit 30 is set if the key was already down when the event was triggered (i.e a key repeat or key up event)
    Bit 31 is set if the key is being pressed (key down)

    You can use the following masks on lParam:
    VB Code:
    1. Public Enum KeyboardHookFlags
    2.     KF_ALTDOWN = &H2000
    3.     KF_DLGMODE = &H800
    4.     KF_EXTENDED = &H100
    5.     KF_MENUMODE = &H1000
    6.     KF_REPEAT = &H4000
    7.     KF_UP = &H8000
    8. End Enum

    To cancel a keyboard event, return 1 from the hook function.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  9. #9

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    Are you sure that is not what I want CornedBee? I really had my hopes up...

    Merrion, I saw that in the MS docs but it does not solve my problem. I can get the keys but I cannot send them or notify my C# program about the keys that are pressed.

    Here is what I have been working on:

    // Hooks up the Message hook
    hMsghook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hDll, 0);
    Code:
    LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
       if(nCode == HC_ACTION)
       {
          if(lParam.Message == WM_KEYDOWN)
          {
    MessageBox(NULL, TEXT("WM_KEYDOWN"), TEXT("KeyPress"), MB_OK);
             // tagMSG(Ptr(lParam)^).hwnd is the handle of the window
             // change 'A' to 'B' system wide
             if(wParam == 65)
             {
                wParam = 66;
             }
          }
       }
       
       return CallNextHookEx(hMsghook, nCode, wParam, lParam);
    }
    I think if I can send the message about the keys that are pressed to a handle of my app it will work.

    If that is so, could you help me get that code working. For one thing lParam.Message does not compile.
    Last edited by aewarnick; Jun 11th, 2003 at 05:05 PM.

  10. #10
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    If you only want to be notified when a given key is pressed, I have just posted a system wide hotkey control (VB.Net - but you should be able to C# it pretty easily) in the code library....
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  11. #11

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    I will try out the hot key. But I was really hoping I could learn how to make the code work above.

    I had my hopes set on the code above and was all excited when I found it. I think it might work. I just need some help getting the code to work and sending a message to my C# app from the message Callback in the unmanaged dll.

    Would you help me get that working if you think it might work?

    I have more of the code working:
    Code:
    LPMSG lpMsg = (LPMSG) lParam;
          if(lpMsg->message == WM_KEYDOWN)
          {
             if(lpMsg->wParam == 65)
             {
    MessageBox(NULL, TEXT("WM_KEYDOWN"), TEXT("KeyPress"), MB_OK);
                lpMsg->wParam = 66;
             }
          }
    I could not figure out how to get a true statement from:
    if(nCode == HC_ACTION) so I just left it out??

    Also, for some reason, the key I am really looking to grab is the print screen key (44) but it does not enter the block like 65 does and other keys I have had work??

    Those two questions and this one I think are all I need to get this working.
    How do I send a message to my app with a key? DispatchMessage();?
    Last edited by aewarnick; Jun 12th, 2003 at 08:57 AM.

  12. #12
    PowerPoster
    Join Date
    Jul 2002
    Location
    Dublin, Ireland
    Posts
    2,148
    When you install a WH_GETMESSAGE hook you are telling the operating system "please run my code whenever an application takes a message of its message queue before giving it that message".

    I'm guessing that when the PrintScreen key is pressed then this is handled by the operating system first and not passed on to any application? After all, no matter what application is the foreground app. PrintScreen always copies a snapshot of the entire desktop.

    There are two ways around this that I can see. First you can set PrintScreen as a system wide hotkey (as earlier post) but in this case I don't think you can change the key press.

    Al;ternatively if you are using Windows NT, 2000, XP then you can install a WH_KEYBOARD_LL hook. This gets called when a keypress is translated by the keyboard driver and therefore you can intercept and change PrintScreen.

  13. #13

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    All I need now is a way to send the wParam to my C# app...
    Code:
    LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {           
        if (((DWORD)lParam & 0x40000000) &&(HC_ACTION==nCode))
        {     
            if (wParam==VK_SNAPSHOT)
            {
                //this is where I send the wParam to
               //my C# app.  How?
            }
        }
            
       return CallNextHookEx(hKeyhook, nCode, wParam, lParam);
    }
    I could also send the wParam from the Message hook instead. I just need help on how to send it.
    Last edited by aewarnick; Jun 12th, 2003 at 10:37 PM.

  14. #14
    PowerPoster
    Join Date
    Jul 2002
    Location
    Dublin, Ireland
    Posts
    2,148
    Since wParam will only ever be VK_SNAPSHOT you don't actually need to send wParam - just notify your C# app that it has been spotted.

    The way I would do this is to have the C# app create a wait handle and pass that to the dll. The dll will need to store this handle in shared memory (i.e. all instances of the dll have access to the same variable) and when the keypress occurs simply set the waithandle to the signalled state.

    Then in your c# program you have a thread that simply waits on that handle beinng set to signalled which resets it to unsignalled and raises the "PrintScreen key pressed" event.

    See the MSDN documentation on: RegisteredWaitHandle (C#) and SetEvent (C/C++) for details...

  15. #15

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    I already have my program all set up to recieve a Message object from the global hook dll. If I used what you are suggesting then I would have to do more coding in C++ if I wanted to watch for another key and then recreate the dll.

    But if I just send all the windows messages to my C# program, the dll would be complete and I would not have to mess with it anymore.

    Isn't there a way in c++ to send a windows message to a certain handle?

  16. #16
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    With sufficient patience and time anything is possible - but this is quite difficult.

    See - when you install a system wide hook the dll that your hook proc is loaded into the address space of each running application. When the event you are hooking occurs then the control is passed to the instance of the dll that is loaded in the application that would recieve that message - which is not neccesarily the instance loaded in your C# application. This means that to communicate with the C# program you have to do some form of inter process communication.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  17. #17

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    I am going to get all my ideas out...

    I found this on the net today and was wondering why this does not work.

    ::SendMessage(RecieverHwnd, WM_KEYDOWN, wParam, lParam);

    How I use it:
    When I set the hook I send the handle of my C# app to the hook dll as a global HWND variable called RecieverHwnd.

    1:
    Isn't that function supposed to send a message to any app? Or am I mistaken?

    2:
    Is it possible that this could be done if I linked another dll to the C++ hooking dll or would that also be injected into the programs?

    3:
    If that won't work, is there some kind of event handler for when a text file changes I could use in the C# program?

    4:
    Or a registry changed handler?

    5:
    In the case of SetEvent for the C++ dll, how would I make the handle variable shared?
    Last edited by aewarnick; Jun 13th, 2003 at 09:58 PM.

  18. #18
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    For the C# program, make a class that inherits from NativeWindow and use the handle from that.

    For the C++ program put the declaration of the variable in a shared emmeory section using #pragma


    #pragma section("shared")
    int hwndReciever


    This variable will be shared across all instances of the DLL.
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  19. #19

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    Thank you Merrion. I would like to learn some things. Could you answer the other questions?

  20. #20
    Frenzied Member MerrionComputin's Avatar
    Join Date
    Apr 2001
    Location
    Dublin, Ireland
    Posts
    1,616
    1 - No, it only send the message to the window whose handle is held in the parameter RecieverHwnd

    2 - If you are using windows messages for communication (see Inter process communication using registered messages from Visual Basic) then you only need one copy of the reciever app - all the injected senders can use that

    For 3 & 4 there's a File System Watcher component ib Visual Studio.NET

    I'd imagine a registry watch component also exists - perhaps have a look on CodeProject?
    ----8<---------------------------------------
    NEW - The .NET printer queue monitor component
    ----8<---------------------------------------
    Now with Examples of use

  21. #21

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    Note: I am not using KeyBoardProc right now. The sending code is in GetMsgProc.

    Have a look. It is not working for some reason.
    Attached Files Attached Files

  22. #22
    PowerPoster
    Join Date
    Jul 2002
    Location
    Dublin, Ireland
    Posts
    2,148
    I'm having trouble getting this into a dll - but my C++ environment is pretty snafu at the moment so it's probably this end.
    Could you post the header and .def files for me too?

    Noe that I recommend you remove "MessageBox" for debugging pruproses and use "OutputDebugString" instead. The default trace listener in C# will then pick up these messages and display them in the IDE.

    e.g.
    Code:
    //---------------------------
    LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {           
        if (((DWORD)lParam & 0x40000000) && (HC_ACTION==nCode))
        {   
            if(aHookID != 0)
            {
                ::SendMessage(RecieverHwnd, aHookID, wParam, lParam);
                OutputDebugString("Should have gotten kbd hook");
            }
                        
            ofstream keysText("file1.txt", ios::app);
                       
            keysText << wParam << "\n" << aHookID << aHook << "\n\n";
            keysText.close();
            
            if (wParam==44) //VK_SNAPSHOT
            {
               OutputDebugString("Should have gotten it!");
            }
        }
            
       return CallNextHookEx(hKeyhook, nCode, wParam, lParam);  //CallNextHookEx returns long value.
    }

  23. #23

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    There. I zipped the whole folder up.
    Attached Files Attached Files

  24. #24
    Fanatic Member McCain's Avatar
    Join Date
    Jan 2002
    Location
    Sweden/Denmark
    Posts
    802
    aewarnick: Did you manage to solve this? If so, how did you do it? I'm having problems very simular to yours...
    Never argue with fools, they will only drag you down to their level, and beat you with experience.

    Q: How do you tell an experienced hacker from a novice?
    A: The latter thinks there's 1000 bytes in a kilobyte, while the former is sure there's 1024 meters in a kilometer

  25. #25

    Thread Starter
    Frenzied Member aewarnick's Avatar
    Join Date
    Dec 2002
    Posts
    1,037
    No I didn't, sorry. Good luck!

  26. #26
    Fanatic Member McCain's Avatar
    Join Date
    Jan 2002
    Location
    Sweden/Denmark
    Posts
    802
    Too bad...
    Guess I'm on my own then
    Never argue with fools, they will only drag you down to their level, and beat you with experience.

    Q: How do you tell an experienced hacker from a novice?
    A: The latter thinks there's 1000 bytes in a kilobyte, while the former is sure there's 1024 meters in a kilometer

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width