分类目录归档:C++

Qt Windows CMakeLists.txt 注意事项

改为32位GUI启动

默认会出现命令行黑窗口,需要添加图形界面类型

if(WIN32)
    set(GUI_TYPE WIN32)
elseif(APPLE)
    set(GUI_TYPE MACOSX_BUNDLE)
endif()

add_executable(${CMAKE_PROJECT_NAME} ${GUI_TYPE} ${SRC} ${RES})

为QT应用添加管理员权限

set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\"")

解决复制dll错误问题

第一次复制成功后,注释以下代码

    foreach (QT_LIB ${REQUIRED_LIBS})
        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                COMMAND ${CMAKE_COMMAND} -E copy
                "${QT_INSTALL_PATH}/bin/Qt${QT_VERSION}${QT_LIB}${DEBUG_SUFFIX}.dll"
                "$<TARGET_FILE_DIR:${PROJECT_NAME}>")
    endforeach (QT_LIB)

设置CMAKE_PREFIX_PATH路径

set(CMAKE_PREFIX_PATH "D:\\Qt\\Qt5.12.9\\5.12.9\\msvc2017\\")

捕获Qt Crash全图教程

关键点

  • 在main.cpp引入SetUnhandledExceptionFilter程序奔溃时生成dmp文件(代码)
  • Build release时包含debug信息(图)
  • Link dbghelp.lib库(图)
  • Build post自动复制qt依赖库(图)

Qt捕获Crash示例代码下载

Qt捕获Crash关键代码

main.cpp

#include <QtWidgets/QApplication>
#include <QMessageBox>
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include "QtCrashApplication.h"
#include "main.h"

#ifdef Q_OS_WIN
#include <Windows.h>
#include <DbgHelp.h>
#endif

#ifdef Q_OS_WIN
static LONG WINAPI exceptionCallback(struct _EXCEPTION_POINTERS* exceptionInfo)
{
    QCoreApplication* app = QApplication::instance();

    QString savePath = app->applicationDirPath() + "dump/";
    qDebug() << "save path :" << savePath;
    QDir dir(savePath);
    if (!dir.exists() && !dir.mkpath(savePath)) {
        app->exit(E_UNEXPECTED);
        return EXCEPTION_EXECUTE_HANDLER;
    }

    savePath.append("assit_");
    savePath.append(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
    savePath.append(".dmp");

    HANDLE dump = CreateFileW(savePath.toStdWString().c_str(), GENERIC_WRITE,
        0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == dump) {
        app->exit(E_UNEXPECTED);
        return EXCEPTION_EXECUTE_HANDLER;
    }

    MINIDUMP_EXCEPTION_INFORMATION miniDumpExceptionInfo;
    miniDumpExceptionInfo.ExceptionPointers = exceptionInfo;
    miniDumpExceptionInfo.ThreadId = GetCurrentThreadId();
    miniDumpExceptionInfo.ClientPointers = TRUE;
    DWORD idProcess = GetCurrentProcessId();
    MiniDumpWriteDump(GetCurrentProcess(), idProcess, dump,
        MiniDumpNormal, &miniDumpExceptionInfo, NULL, NULL);

    CloseHandle(dump);

    app->exit(E_UNEXPECTED);
    return EXCEPTION_EXECUTE_HANDLER;
}
#endif

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

#ifdef Q_OS_WIN
    SetUnhandledExceptionFilter(exceptionCallback);
#endif

    QtCrashApplication w;
    w.show();
    return a.exec();
}

QtCrashApplication.cpp

#include "QtCrashApplication.h"

QtCrashApplication::QtCrashApplication(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    QObject::connect(ui.CrashButton, SIGNAL(clicked()), this, SLOT(crashMe()));
}

void QtCrashApplication::crashMe() {
    int i = 0;
    int n = 1 / i;
    ui.CrashButton->setText(QString(n));
}

新建项目

新建项目

配置项目

配置项目

项目配置向导

项目配置向导

项目配置向导2

项目配置向导2

编写会奔溃程序

编写会奔溃程序

程序运行截图

程序运行截图

点击运行后出现异常

点击运行后出现异常

自动生成依赖qt库

"$(QTDIR)\bin\windeployqt.exe" "$(OutDir.TrimEnd('\'))" --$(Configuration.toLower())

自动生成依赖qt库

自动生成依赖库

自动生成依赖库

生成release时生成debug信息

生成release时生成debug信息

包含dbghelp.lib库

包含dbghelp.lib库

查看生成记录

查看生成记录

查看结果

查看结果

参考

std::lock_guard — C++最简单的线程安全锁(避免死锁)

std::lock_guard 简单用法

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int g_count = 0;
mutex g_mutex;

void increment()
{
    lock_guard<mutex> lock(g_mutex); // 开启后g_count总是可以输出20000,否则会少于20000
    ++g_count;
    this_thread::sleep_for(chrono::microseconds(2));
}

void run(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment();
    }
}

int main()
{

    thread th1(run, 10000);
    thread th2(run, 10000);

    th1.join();
    th2.join();

    cout << g_count;

    return 0;
}

直接使用lock容易不对称导致死锁

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int g_count = 0;
mutex g_mutex;

int increment1()
{
    g_mutex.lock();
    this_thread::sleep_for(chrono::microseconds(1));
    g_count++;
    g_mutex.unlock();
    return 0;
}

int increment2()
{
    g_mutex.lock();
    this_thread::sleep_for(chrono::microseconds(1));
    if (g_count > 11000)
    {
        return 0;
    }
    g_count++;
    g_mutex.unlock();
    return 0;
}

void run1(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment1();
    }
}

void run2(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment2();
    }
}

int main()
{

    thread th1(run1, 10000);
    thread th2(run2, 10000);

    th1.join();
    th2.join();

    cout << g_count;

    return 0;
}

使用lock_guard避免死锁

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int g_count = 0;
mutex g_mutex;

int increment1()
{
    lock_guard<mutex> lock(g_mutex);
    this_thread::sleep_for(chrono::microseconds(1));
    g_count++;
    return 0;
}

int increment2()
{
    lock_guard<mutex> lock(g_mutex);
    this_thread::sleep_for(chrono::microseconds(1));
    g_count++;
    if (g_count > 11000)
    {
        return 0;
    }
    return 0;
}

void run1(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment1();
    }
}

void run2(int times)
{
    for (int i = 0; i < times; i++)
    {
        increment2();
    }
}

int main()
{

    thread th1(run1, 10000);
    thread th2(run2, 10000);

    th1.join();
    th2.join();

    cout << g_count;

    return 0;
}

防止list下标越界,同样适用于map,set,vector等

#include <iostream>
#include <thread>
#include <mutex>
#include <list>

using namespace std;

class MonitorList
{

private:
    mutex g_mutex;
    list<int> g_list = {};

public:
    void push()
    {
        lock_guard<mutex> lock(g_mutex); // 不加会导致程序异常
        this_thread::sleep_for(chrono::microseconds(1));
        g_list.push_back(0);
    }

    void pop()
    {
        lock_guard<mutex> lock(g_mutex); // 不加会导致程序异常
        this_thread::sleep_for(chrono::microseconds(1));
        for (auto it = g_list.begin(); it != g_list.end(); ++it)
        {
            if (*it % 2 == 0)
            {
                it = g_list.erase(it);
            }
        }
    }
    int size()
    {
        return g_list.size();
    }
};

MonitorList g_MonitorList;

int main()
{

    thread th1([](int n)
               {
                   for (int i = 0; i < n; i++)
                   {
                       g_MonitorList.push();
                   }
               },
               10000);

    thread th2([](int n)
               {
                   for (int i = 0; i < n; i++)
                   {
                       g_MonitorList.pop();
                   }
               },
               3000);

    thread th3([](int n)
               {
                   for (int i = 0; i < n; i++)
                   {
                       g_MonitorList.pop();
                   }
               },
               3000);
    th1.join();
    th2.join();
    th3.join();

    cout << g_MonitorList.size() << endl;

    return 0;
}

使用MSYS2+mingw+QT作为C++开发环境

下载msys2

在清华大学大学镜像站点https://mirrors.tuna.tsinghua.edu.cn/msys2/distrib/下载最新版32(i686)/64(x86_64)位系统msys2

下载后安装

pacman 的配置

msys2采用pacman管理软件包,修改pacman的软件源提高下载速度

编辑 /etc/pacman.d/mirrorlist.mingw32 ,在文件开头添加:

Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686

编辑 /etc/pacman.d/mirrorlist.mingw64 ,在文件开头添加:

Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64

编辑 /etc/pacman.d/mirrorlist.ucrt64 ,在文件开头添加:

Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/ucrt64

编辑 /etc/pacman.d/mirrorlist.clang64 ,在文件开头添加:

Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/clang64

编辑 /etc/pacman.d/mirrorlist.msys ,在文件开头添加:

Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch

然后执行 pacman -Sy 刷新软件包数据即可。

pacman -Sy

安装开发环境

安装基本开发环境和常用工具

pacman -S  base-devel  git  wget  p7zip  perl ruby python

安装 32位 MinGW-w64

pacman -S  mingw-w64-i686-toolchain

安装 64位 MinGW-w64

pacman -S  mingw-w64-x86_64-toolchain

安装Qt5和Qt Creator

pacman -S mingw64/mingw-w64-x86_64-qt5 mingw64/mingw-w64-x86_64-qt-creator

参考

C++学习资料

电子书

纸质书

Windows自动化打印Office文件(Word,PDF,Excel,PPT等)

Windows(C++)

WPF(.net C#)

打印机自助打印代码(队列监控拦截+取消+状态获取)(附C#,C++,VB实现)

Monitor jobs in a printer queue (.NET)

https://www.codeproject.com/Articles/51085/Monitor-jobs-in-a-printer-queue-NET

c# 完整代码展示了如何获取打印机任务状态

file

Print Spooler API

https://docs.microsoft.com/zh-cn/windows/win32/printdocs/print-spooler-api

The Print Spooler API provides an interface to the print spooler for applications to manage printers and print jobs.

The Print Spooler API is used by an application as part of its programming and not directly by end users.

PrintQueue.GetPrintJobInfoCollection Method

Creates a collection that contains a PrintSystemJobInfo object for each job in the queue.

https://docs.microsoft.com/en-us/dotnet/api/system.printing.printqueue.getprintjobinfocollection?view=netframework-4.8

"Virtual Printer" or "Moving Printjobs" (C++)

https://cboard.cprogramming.com/windows-programming/108156-virtual-printer-moving-printjobs.html

Monitoring a Printer Queue from VB.NET

https://www.codeproject.com/Articles/3313/Monitoring-a-Printer-Queue-from-VB-NET

How to catch printer event in python

https://stackoverflow.com/questions/15748386/how-to-catch-printer-event-in-python

Printers and SafeHandles

https://www.codeproject.com/Articles/14690/Printers-and-SafeHandles

Printers and SafeHandles (Part 2)

https://www.codeproject.com/Articles/15084/Printers-and-SafeHandles-Part-2