Developing Applications with Qt

Qt is a powerful framework for developing cross-platform applications, offering extensive libraries and tools to streamline the development process. This essay provides an overview of essential techniques and best practices for working with Qt, including managing dependencies, handling permissions, incorporating icons, managing translations, enforcing single-instance applications, and implementing basic encryption.

Including Qt Headers

When including Qt headers, if you encounter compilation success but linkage failure, it may be necessary to specify the appropriate namespaces in your project file (.pro). For example:

QT += core gui
QT += network
QT += xml
QT += webkit

These lines ensure that the necessary Qt modules are linked correctly during the build process.

Obtaining Administrator Privileges

To grant your executable administrative privileges on Windows, you can modify the project file (.pro) as follows:

QMAKE_LFLAGS += /MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\"

This line modifies the linker flags to request elevated permissions when the application is run.

Adding Application Icons

To add an icon to your Qt application, include the following lines in your project file:

RC_FILE = icon.rc
OTHER_FILES += \icon.rc

This configuration ensures that the specified icon resource is included in the build process and linked with the application.

Adding Library Dependencies

Managing dependencies in Qt projects involves specifying include paths and library paths. Update your project file (.pro) to include necessary libraries as shown:

INCLUDEPATH += $$PWD/../ext/win/include $$PWD/../../boost_1_40_0/boost/property_tree/detail $$PWD/../lib $$PWD/../../boost_1_40_0 $$PWD/../curl $$PWD/../
DEPENDPATH += $$PWD/../ext/win/x86 $$PWD/../../boost_1_40_0/libx86/lib
win32:CONFIG(release, debug|release): {
LIBS += -L$$PWD/../ext/win/x86/ -L$$PWD/../../boost_1_40_0/libx86/lib -L$$PWD/../curl/lib -L$$PWD/plugins/imageformats -lxmlrpc_Win32_Release -lsqlite3_Win32_Release -lWS2_32 -lIPHlpApi -lMpr -lPowrProf \
-libboost_filesystem-vc90-mt-1_42 -lAdvapi32 -lKernel32 -lUser32 -lqgif4 -llibcurl -lws2_32 -llibeay32 -lssleay32
}
else:win32:CONFIG(debug, debug|release): {
LIBS += -L$$PWD/../ext/win/x86/ -L$$PWD/../../boost_1_40_0/libx86/lib -L$$PWD/../curl/lib -L$$PWD/plugins/imageformats -lxmlrpc_Win32_Debug -lsqlite3_Win32_Debug -lWS2_32 -lIPHlpApi -lMpr -lPowrProf \
-libboost_filesystem-vc90-mt-gd-1_42 -lAdvapi32 -lKernel32 -lUser32 -lqgifd4 -llibcurld -lws2_32 -llibeay32 -lssleay32
}

This configuration helps in resolving dependencies by specifying the paths for include files and libraries.

Internationalization

Internationalizing a Qt application involves preparing the project for translations. First, add the following line to the project file to include the translation file:

TRANSLATIONS += scc.ts

In the application code, wrap translatable strings with the tr() function. To generate and compile translation files, use the lupdate and lrelease commands. At the program entry point, load the appropriate translation file based on the selected language:

QTranslator qtTranslator;
if(lang == LANG_EN){
} else if(lang == LANG_ZH){
    qtTranslator.load("scc.qm");
    a.installTranslator(&qtTranslator);
}

Enforcing Single-Instance Applications

To ensure that only one instance of an application runs at a time, implement a singleton pattern. Below is a typical implementation:

#ifndef SINGLEINSTANCE_H
#define SINGLEINSTANCE_H
#if WIN32
#include <windows.h>
#include <string>
#include "util.h"

using std::string;

class CLimitSingleInstance {
protected:
    DWORD m_dwLastError;
    HANDLE m_hMutex;

public:
    CLimitSingleInstance(string strMutexName) {
        string name = "Global\\" + strMutexName;
        m_hMutex = CreateMutex(NULL, TRUE, s2ws(name).c_str());
        m_dwLastError = GetLastError();
    }

    ~CLimitSingleInstance() {
        if (m_hMutex) {
            CloseHandle(m_hMutex);
            m_hMutex = NULL;
        }
    }

    BOOL IsAnotherInstanceRunning() {
        return (ERROR_ALREADY_EXISTS == m_dwLastError || m_hMutex == 0);
    }
};

static CLimitSingleInstance g_SingleShineCloudClientInstanceObj("{F5A38595-26E4-4999-AB15-395BC6B23402}");

static inline bool isAnotherSCCRunning() {
    return g_SingleShineCloudClientInstanceObj.IsAnotherInstanceRunning();
}

#else
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include "db.h"
#include <stdlib.h>
#include "sutil.h"
#include <errno.h>

using std::string;

struct flock* file_lock(short type, short whence) {
    static struct flock ret;
    ret.l_type = type;
    ret.l_start = 0;
    ret.l_whence = whence;
    ret.l_len = 0;
    ret.l_pid = getpid();
    return &ret;
}

class CLimitSingleInstance {
    string filename;
    int fp;
    bool isLock;

public:
    CLimitSingleInstance(string file) : isLock(false), filename(file), fp(open(file.c_str(), O_CREAT | O_RDWR)) {
        if(fp != -1 && fcntl(fp, F_SETLK, file_lock(F_WRLCK, SEEK_SET)) != -1) {
            isLock = true;
        }
    }

    CLimitSingleInstance() : isLock(false) {
        const char* id = DBGetOptionValue("host_cpid");
        string appPath = suGetApplicationPath() + "/locks/";
        system(("mkdir -p " + appPath).c_str());
        appPath += id;
        filename = appPath;
        fp = open(filename.c_str(), O_CREAT | O_RDWR);
        if(fp != -1) {
            system(("chmod u+rw \"" + filename + "\"").c_str());
            if(fcntl(fp, F_SETLK, file_lock(F_WRLCK, SEEK_SET)) != -1) {
                isLock = true;
            }
        }
        DBReleaseOptionValue(id);
    }

    ~CLimitSingleInstance() {
        if(fp != -1) {
            fcntl(fp, F_SETLK, file_lock(F_UNLCK, SEEK_SET));
            close(fp);
        }
    }

    bool IsAnotherInstanceRunning() {
        return (fp == -1) ? false : !isLock;
    }
};

static inline bool isAnotherSlaveRunning() {
    static CLimitSingleInstance g_SingleShineWonderSlaveInstanceObj;
    return g_SingleShineWonderSlaveInstanceObj.IsAnotherInstanceRunning();
}

#endif
#endif // SINGLEINSTANCE_H

Basic Encryption

For basic encryption, you can use the following simple encryption class:

#ifndef SIMPLECRYPT_H
#define SIMPLECRYPT_H

#include <QString>
#include <QVector>
#include <QFlags>

class SimpleCrypt {
public:
    enum CompressionMode { CompressionAuto, CompressionAlways, CompressionNever };
    enum IntegrityProtectionMode { ProtectionNone, ProtectionChecksum, ProtectionHash };
    enum Error { ErrorNoError, ErrorNoKeySet, ErrorUnknownVersion, ErrorIntegrityFailed };

    SimpleCrypt();
    explicit SimpleCrypt(quint64 key);
    void setKey(quint64 key);
    bool hasKey() const { return !m_keyParts.isEmpty(); }
    void setCompressionMode(CompressionMode mode) { m_compressionMode = mode; }
    CompressionMode compressionMode() const { return m_compressionMode; }
    void setIntegrityProtectionMode(IntegrityProtectionMode mode) { m_protectionMode = mode; }
    IntegrityProtectionMode integrityProtectionMode() const { return m_protectionMode; }
    Error lastError() const { return m_lastError; }
    QString encryptToString(const QString& plaintext);
    QString encryptToString(QByteArray plaintext);
    QByteArray encryptToByteArray(const QString& plaintext);
    QByteArray encryptToByteArray(QByteArray plaintext);
    QString decryptToString(const QString& cyphertext);
    QByteArray decryptToByteArray(const QString& cyphertext);
    QString decryptToString(QByteArray cypher);
    QByteArray decryptToByteArray(QByteArray cypher);

    enum CryptoFlag { CryptoFlagNone = 0, CryptoFlagCompression = 0x01, CryptoFlagChecksum = 0x02, CryptoFlagHash = 0x04 };
    Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag)

private:
    void splitKey();
    quint64 m_key;
    QVector<char> m_keyParts;
    CompressionMode m_compressionMode;
    IntegrityProtectionMode m_protectionMode;
    Error m_lastError;
};

Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags