arnm
arnm

Reputation: 1765

unresolved external symbol public __thiscall only in debug mode

I have looked around for posts trying to solve this error but in every case I am already doing what they have suggested.

My compile output:

main.obj:-1: error: LNK2019: unresolved external symbol "public: __thiscall KeyLogger::~KeyLogger(void)" (??1KeyLogger@@QAE@XZ) referenced in function _main

main.obj:-1: error: LNK2019: unresolved external symbol "public: __thiscall KeyLogger::KeyLogger(void)" (??0KeyLogger@@QAE@XZ) referenced in function _main

debug\AccipioKeyDemo.exe:-1: error: LNK1120: 2 unresolved externals

I know that this is saying that I have the KeyLogger constructor and destructor defined but not implemented but I actually do have everything implemented.

main.cpp

#include <QCoreApplication>
#include "keylogger.h"

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    KeyLogger k;

    return a.exec();
}

keylogger.h

#ifndef KEYLOGGER_H
#define KEYLOGGER_H

#include <Windows.h>

class KeyLogger {

public:
    KeyLogger();
    ~KeyLogger();

    void start();
    void stop();

private:
    HHOOK hook;

    LRESULT CALLBACK intercept(int code, WPARAM wparam, LPARAM lparam);
};

#endif // KEYLOGGER_H

keylogger.cpp

#include "keylogger.h"
#include <QDebug>

KeyLogger::KeyLogger() : hook(NULL) {
    hook = SetWindowsHookEx(WH_KEYBOARD_LL, intercept, NULL,0);

    if (hook == NULL) {
        qDebug() << "HOOK FAILED";
    } else {
        qDebug() << "HOOK SUCCESS";
    }
}

KeyLogger::~KeyLogger() {

}

void KeyLogger::start() {
    qDebug() << "start";
}

void KeyLogger::stop() {
    qDebug() << "stop";
}

LRESULT CALLBACK KeyLogger::intercept(int code, WPARAM wparam, LPARAM lparam) {
    qDebug() << "Key Pressed";
    return CallNextHookEx(hook, code, wparam, lparam);
}

QT Pro config

#-------------------------------------------------
#
# Project created by QtCreator 2013-10-10T19:58:51
#
#-------------------------------------------------

QT       += core

QT       -= gui

TARGET = AccipioKeyDemo
CONFIG   += console
CONFIG   -= app_bundle

LIBS += user32.lib

TEMPLATE = app

SOURCES += main.cpp \
    keylogger.cpp

HEADERS += \
    keylogger.h

Upvotes: 0

Views: 2214

Answers (2)

Your code is broken because callback methods must be static members - essentially they have to be free functions. Since there's no way to pass the pointer to KeyLogger instance to the intercepting callback function, your hook must be a class member, not instance member. It's perhaps not that bad of an idea to protect the hook with a mutex, in case you later forgot yourself and tried to instantiate KeyLoggers in multiple threads.

It's also an error, in your case, for the KeyLogger object to be copyable. Use Q_DISABLE_COPY macro on classes that are not meant to be copied.

You may want to delete the build directory and build your project again, but please remember to fix the bugs as shown below or it won't work.

The minimized version below (just a main.cpp file) works and compiles just fine. It demonstrates how to properly handle dumping data from the event queue: you must copy a small chunk of the data out of the queue while holding the mutex, then release the mutex and only then dump it elsewhere.

#include <QCoreApplication>
#include <QMutex>
#include <QDebug>
#include <QQueue>
#include <QDataStream>
#include <windows.h>

struct KeyLoggerEvent {
   WPARAM event;
   KBDLLHOOKSTRUCT key;
   KeyLoggerEvent(WPARAM ev, KBDLLHOOKSTRUCT k) : event(ev), key(k) {}
};
QDataStream & operator<<(QDataStream & s, const KeyLoggerEvent & kev) {
   s << kev.event
     << (quint32)kev.key.flags << (quint32)kev.key.scanCode
     << (quint32)kev.key.time << (quint32)kev.key.vkCode;
   return s;
}

class KeyLogger {
   Q_DISABLE_COPY(KeyLogger)
   static QMutex m_hookMutex;
   static HHOOK m_hook;
   static QQueue<KeyLoggerEvent> m_events;
   static LRESULT CALLBACK intercept(int code, WPARAM wparam, LPARAM lparam);
public:
   KeyLogger() {
      QMutexLocker lock(&m_hookMutex);
      Q_ASSERT(!m_hook);
      m_hook = SetWindowsHookEx(WH_KEYBOARD_LL, intercept, NULL,0);
      if (!m_hook) qDebug() << "HOOK FAILED";
      lock.unlock();
   }
   ~KeyLogger() {
      QMutexLocker lock(&m_hookMutex);
      if (m_hook) UnhookWindowsHookEx(m_hook);
      m_hook = NULL;
   }
   //! Dumps a bunch of events to the stream. Returns false if no more events remain in the
   //! log. To avoid lock contention, it keeps the queue lock for a very short amount of time.
   bool dump(QDataStream & s) {
      int batchCount = 1000;
      QQueue<KeyLoggerEvent> dumpQueue;
      QMutexLocker lock(&m_hookMutex);
      while (batchCount-- && !m_events.empty()) {
         dumpQueue.enqueue(m_events.dequeue());
      }
      bool more = !m_events.empty();
      lock.unlock();
      // The below could block for a long time, thus it works from a local copy.
      while (! dumpQueue.empty()) s << dumpQueue.dequeue();
      return more;
   }
};

QMutex KeyLogger::m_hookMutex;
HHOOK KeyLogger::m_hook = NULL;
QQueue<KeyLoggerEvent> KeyLogger::m_events;

LRESULT CALLBACK KeyLogger::intercept(int code, WPARAM wparam, LPARAM lparam) {
   qDebug() << "Key Event";
   QMutexLocker lock(&m_hookMutex);
   if (code >= 0) {
      KBDLLHOOKSTRUCT * key = reinterpret_cast<KBDLLHOOKSTRUCT*>(lparam);
      m_events.enqueue(KeyLoggerEvent(wparam, *key));
   }
   HHOOK hook = KeyLogger::m_hook;
   lock.unlock();
   return CallNextHookEx(hook, code, wparam, lparam);
}

int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   KeyLogger k;
   return a.exec();
}

Upvotes: 2

Karadur
Karadur

Reputation: 1246

You're getting the linkage error, where the linker cannot find an obj file with constructor and destructor. That means keylogger.cpp is either not compiled or linker cannot find its obj file. Check the settings of your project.

Upvotes: 0

Related Questions