Doing a QList::append while using KBDLLHOOKSTRUCT (WinAPI) results in segmentation fault. :(



  • Hello dear Qt community,

    as a little exercise, I wanted to write a class which logs every key press made on the keyboard (No, I'm really not planning to do illegal stuff with it.). Because I didn't like Qt's way, I decided to use the WinAPI code of this guy here to catch the strokes: https://www.youtube.com/watch?v=O0C4V6JmlNw.
    But now I have a persistent crash when appending my gained information to a QList in the KeyBoardProc function. Showing it with qDebug() works though.

    This is the error I get from the debugger:

    template <typename T>
    Q_OUTOFLINE_TEMPLATE void QList<T>::append(const T &t)
    {
            if  (d->ref.isShared()) {
            Node *n = detach_helper_grow(INT_MAX, 1);
            QT_TRY {
                node_construct(n, t);
            } 
    [...]
    }
    

    (The yellow arrow of the debugger points to the fourth line (QList.h, line 577).)

    I already found out that it doesn't matter what kind of variable I'm trying to append or if I even append a constant value; it crashes when using append. Well, of course I noticed that obviously some reference is shared which isn't supposed to be shared. So instead of plain appending, I tried to make something like this

    append(QList()<<value)
    

    but that just gave me another error message:

    template <typename T>
    Q_OUTOFLINE_TEMPLATE QList<T> &QList<T>::operator+=(const QList<T> &l)
    {
        if (!l.isEmpty()) {
            if (d == &QListData::shared_null) {
                *this = l;
            }
    [...]
    }
    

    (This time, the yellow arrow of the debugger points to the fifth line (QList.h, line 938).)

    Well, to be honest I'm at the end of my knowledge of Qt. I just know that I seem to not be allowed to pass a certain reference and because I violate against this rule, shit goes down. Trying to make a deep copy via temporary saving the data in another variable doesn't work either. Well, the assignment works but the appending fucks it up lets it crash again.

    So, here's the simplified code of my class. I hope you can find a solution! Thanks

    KeyLog.h

    #ifndef KEYLOG_H
    #define KEYLOG_H
    
    #include <iostream>
    #include <QCoreApplication>
    #include <QDateTime>
    #include <QDebug>
    #include <windows.h>
    
    
    namespace WindowsAction
    {
        class KeyLog : public QObject
        {
            Q_OBJECT
    
        protected:
        //Variablen
            bool Active_;               //loggen (true) oder inaktiv sein (false)
            HHOOK hHook;                //Hook (Low-Level-Verbindung mit der Tastatur)
            QList<QDateTime> DTLog_;    //Log (DateTime)
            QList<int>  NrLog_;         //Log (Key-Nummer)
            QStringList NameLog_;       //Log (Key-Name)
    
        //Funktionen
            LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam);  //Hook-Funktion
    
        public:
            KeyLog(const bool& Active=true);
            ~KeyLog();
    
            QList<QDateTime> DTLog   ()             const;
            QDateTime        DTLog   (const int& i) const;
            bool             isActive()             const;
            bool             isEmpty ()             const;
            QList<int>       NrLog   ()             const;
            int              NrLog   (const int& i) const;
            QStringList      NameLog ()             const;
            QString          NameLog (const int& i) const;
    
        signals:
            void KeyPressed (QString Name, int Nr, QDateTime DT) const;
            void KeyReleased(QString Name, int Nr, QDateTime DT) const;
            void activated()                                     const;
            void deactivated()                                   const;
    
        public slots:
            void activate();        //aktiviere loggen
            void clear();           //lösche Logs
            void deactivate();      //deaktiviere loggen
            void toggle();          //ändere log_
        };
    }
    
    #endif // KEYLOG_H
    

    KeyBoardProc.h

    #include "KeyLog.h"
    
    
    LRESULT CALLBACK WindowsAction::KeyLog::KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        KBDLLHOOKSTRUCT cKey=*((KBDLLHOOKSTRUCT*)wParam);
    
        wchar_t buffer[5];
        BYTE keyboard_state[256];
    
        [...]
    
        //get key name
        char lpszName[0x100]={0};
    
        [...]
    
        GetKeyNameText(dwMsg, (LPTSTR)lpszName, 255);
    
        //try to convert the key info
        ToUnicodeEx(cKey.vkCode,
                    cKey.scanCode,
                    keyboard_state,
                    buffer,
                    4,
                    0,
                    keyboard_layout);
        buffer[4]=L'\0';
    
    
    
        qDebug()<<"key: "<<cKey.vkCode<<" "<<QString::fromUtf16((ushort*)buffer)<<" "<<QString::fromUtf16((ushort*)lpszName);
    
    //*****SHIT GOES DOWN HERE*****
        DTLog_.append(QDateTime::currentDateTime());
        NrLog_.append(QList<int>()<<cKey.vkCode);
        if(QString::fromUtf16((ushort*)buffer).isEmpty()==false) NameLog_.append(QString::fromUtf16((ushort*)buffer));
        else NameLog_.append(QString::fromUtf16((ushort*)lpszName));
    
        return CallNextHookEx(hHook, nCode, wParam, lParam);
    }
    
    //Quelle WinAPI-Zeugs: https://www.youtube.com/watch?v=O0C4V6JmlNw
    

    KeyLog.cpp

    #include "KeyLog.h"
    
    
    //public:
    WindowsAction::KeyLog::KeyLog(const bool& Active)
    {
        if(Active==true) activate();
    }
    
    WindowsAction::KeyLog::~KeyLog()
    {
        if(Active_==true) deactivate();
    }
    
    [...]
    
    //public slots
    void WindowsAction::KeyLog::activate()
    {
        hHook=SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)&KeyBoardProc, NULL, 0);
    
        if(hHook!=NULL)
        {
            Active_=true;
            emit activated();
        }
        else
        {
            Active_=false;
        }
    }
    
    [...]
    
    void WindowsAction::KeyLog::deactivate()
    {
        hHook=NULL;
    
        Active_=false;
        emit deactivated();
    }
    
    [...]
    

    That's all for now. I'm open to any suggestion and really hope this can get fixed quickly. :) Thanks again!

    Felix


  • Qt Champions 2016

    @Thynome
    Hi,
    My "educated" guess is that you have dereferenced an invalid address. Something of the type:

    MyClass * var;
    var->method();
    

    As long as method() doesn't touch anything class-related (i.e. acts as a static function) you'll probably get away with that. Provide a stack trace ('cause that QList reference doesn't mean much) and it will (most probably) get clearer why the crash is occurring.

    Kind regards.


  • Qt Champions 2016

    @Thynome said:

    hHook=SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)&KeyBoardProc, NULL, 0);

    Hi
    Im wondering if SetWindowsHookEx likes a callback that lives in a class ?

    https://msdn.microsoft.com/da-dk/library/windows/desktop/ms644990(v=vs.85).aspx
    "Type: HOOKPROC
    A pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL. Otherwise, lpfn can point to a hook procedure in the code associated with the current process."

    Normally for callback its plain "c" functions/ non member or at least a static member function.


  • Qt Champions 2016

    @mrjj
    Whaddaya know, I turned out more educated than I expected ... good catch sir, I was too lazy to actually trace the code!



  • Thanks for your answer, @kshegunov and @mrjj.

    I moved the function outside of my class and made it a friend so I still have permission to access protected class members. But now I have the problem that I must provide the object to the function... I thought about somehow passing the this pointer to the function but couldn't figure out how. :/ Passing it via parameter is not possible due to the weird function calling and my tries with a (semi) global variable didn't work either..

    Do you have an idea? I would be very happy about any suggestion. :)

    Felix


  • Qt Champions 2016

    @Thynome
    Hi,
    Well, you're stuck with C when working with the WinApi. It sucks, I know, but you need to transfer the context (the object) through a global variable. You can look here for some inspiration - WindowsCtrlDispatcher::instance is the context that's transferred to the VOID WINAPI ServiceMain(DWORD, LPTSTR *) function. I hope that helps.

    Kind regards.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.