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 uplets 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
-
@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 thatQList
reference doesn't mean much) and it will (most probably) get clearer why the crash is occurring.Kind regards.
-
@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.
-
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
-
@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 theVOID WINAPI ServiceMain(DWORD, LPTSTR *)
function. I hope that helps.Kind regards.