0

Subject: substitute certain keys to another key value.

For e.g. if I press P it should be F24.

When I try to load key values from .ini file hook ceases to be a global. It works only if winapi form in focus.

My DLL code:

    extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardHook(int, WPARAM, LPARAM);
    extern "C" __declspec(dllexport) void loadSettings(LPSTR);
    bool shouldUpdateKey = false;

    int ArcherKey;

    LRESULT CALLBACK KeyboardHook(int code, WPARAM wParam, LPARAM lParam)
        {

            if ((lParam >> 20))
            {
                if (wParam == ArcherKey) 
                {
                shouldUpdateKey = shouldUpdateKey ? false : true;
                if (shouldUpdateKey) 
                {
                MessageBox(NULL, L"ArcherKey", L"", MB_OK);
                keybd_event(0x87, 45, 1, 0); //press F24
                return 1; 
                }
                }
            }


            return CallNextHookEx(NULL, code, wParam, lParam);
        }

      LPSTR GetValueFromINI(LPSTR FileName, LPSTR Section, LPSTR Key)
        {
            char *key;
            key = (char *)malloc(256);
            GetPrivateProfileStringA(Section, Key, NULL, key, 256, FileName);
            return key;
            free(key);
        }

      void loadSettings(LPSTR FileName) 
        {
            ArcherKey = atoi(GetValueFromINI(FileName, "HotKey", "Archer key"));
        }

I use shouldUpdateKey to avoid x2 callback (when key pressed down and pressed up) call. Also i try to add this statement if (lParam >>31) ^ 1, but this statement always false.

.exe code:

LRESULT(*pKeybHook)(int, WPARAM, LPARAM);
HHOOK hhookMsg;
void(*loadSettings)(LPSTR);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
/* default code */

HMODULE dll = LoadLibrary(_T("MainHookDLL.dll"));
    if (dll)
    {

        pKeybHook = (LRESULT(*)(int, WPARAM, LPARAM)) GetProcAddress(dll, "_KeyboardHook@12");

        loadSettings = (void(*)(LPSTR)) GetProcAddress(dll, "loadSettings");

        loadSettings("C:\\Settings.ini");

        hhookMsg = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)(pKeybHook), dll, 0);

    }

/* defult code */
    UnhookWindowsHookEx(hhookMsg); // unhook
    FreeLibrary(dll);
    return (int) msg.wParam;
}       

Settings.ini structure:

[HotKey]
Archer key=80

So my problem: If try to load settings from file, hook works only in active winapi window. It shows MessageBox\etc, but only in active winapi form. If replace wParam == ArcherKey to wParam == 80 it works global, in all apps. I debug my application, after load from .ini file i have ArcherKey = 80. So I really can't understand what a mistake I actually have.

RevTheAsker
  • 5
  • 1
  • 3

3 Answers3

1

As far as I recall it, the DLL containing the HOOKPROC is loaded in all other processes if the hook is global. This means you have several instances of your DLL in memory. Since you call loadSettings(..) from within your application the value of ArcherKey is only initialized for that process. Which results in the behavior you're observing.

To change this, you should modify the DllMain(..) function to something like this:

BOOL WINAPI DllMain(HINSTANCE hinstDLL,  // handle to DLL module
                    DWORD fdwReason,     // reason for calling function
                    LPVOID lpReserved )  // reserved
{

   switch( fdwReason ) 
   { 
      case DLL_PROCESS_ATTACH:
         loadSettings("C:\\Settings.ini");
         break;

      case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
         break;

      case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
         break;

      case DLL_PROCESS_DETACH:
         // Perform any necessary cleanup.
         break;
   }

   return TRUE;
}

This initializes the value of ArcherKey for all processes the hook is being installed since Windows calls DllMain when loading a DLL. For testing purposes you may add a MessageBeep(0); before calling loadSettings(..) to verify that the section of code is executed.

Having a quick look at the documentation of SetWindowsHookEx(..) made my fears come true: If you're compiling a 32bit DLL you will not be able to hook 64bit processes and vice-versa. In order to do that you have to implement a 64bit version of the dll with a differently named HOOKPROC.

Lukas Thomsen
  • 3,089
  • 2
  • 17
  • 23
  • Oh, I should read more documentation -.- I just understand this when add MessageBox in DLL entry func, everytime I do keyboard action in a new window dll creates new instance(if this correct meaning). Thx, now I know what should I do. – RevTheAsker Oct 21 '14 at 19:11
0

Hooks are "injected" into other processes, which means your entire DLL will be loaded into all relevant processes, as if the process itself (e.g. Notepad.exe) had called LoadLibrary(). So in that context (in the other process, e.g. Notepad.exe), your settings will not have been loaded, so ArcherKey will not be initialized, so the message box won't appear.

So you have to let your DLL do the initialization, not a separate .exe. You can either initialize ArcherKey (load your settings) via your DllMain on DLL_PROCESS_ATTACH (though there are caveats as to which APIs are safe at that point - mostly any calls which would cause other DLLs to be loaded are a no-no), or you can add code roughly a la:

static DWORD initialized = 0;
static int ArcherKey;

LRESULT CALLBACK KeyboardHook(int code, WPARAM wParam, LPARAM lParam)
{
    if (!initialized)
    {
        loadSettings();
    }

    ...
}

Though this code isn't advisable at all, since long running hooks are at a minimum rather bad form and may cause issues (stalling that process, for example). Alternatively, you could place the data in a known shared location. Edit: there are some good suggestions as to means of sharing values in the accepted answer to a similar question.

Community
  • 1
  • 1
El Zorko
  • 3,349
  • 2
  • 26
  • 34
0
LPSTR GetValueFromINI(LPSTR FileName, LPSTR Section, LPSTR Key)
{
    char *key;
    key = (char *)malloc(256);
    GetPrivateProfileStringA(Section, Key, NULL, key, 256, FileName);
    return key;
    free(key); // <- will never happen
}

ArcherKey = atoi(GetValueFromINI(...)); // <- does not clean up

Memory leak