Commit c9174ca4 authored by captainwong's avatar captainwong

update

parent c024947b
#pragma once #pragma once
#include "qt_global.h" #include "qt_global.h"
#include <system_error> #include <system_error>
#include <QString> #include <QString>
JLIBQT_NAMESPACE_BEGIN JLIBQT_NAMESPACE_BEGIN
//! 获取错误码文字描述 //! 获取错误码文字描述
inline QString ecString(const std::error_code& ec) { inline QString ecString(const std::error_code& ec) {
return QString::fromLocal8Bit(ec.message().data()); return QString::fromLocal8Bit(ec.message().data());
} }
//! 获取错误码文字描述(带有错误目录) //! 获取错误码文字描述(带有错误目录)
inline QString ecStringWithCategory(const std::error_code& ec) { inline QString ecStringWithCategory(const std::error_code& ec) {
return QString::fromLocal8Bit(ec.category().name()) + ": " + QString::fromLocal8Bit(ec.message().data()); return QString::fromLocal8Bit(ec.category().name()) + ": " + QString::fromLocal8Bit(ec.message().data());
} }
#define BREAK_IF_QUERY_FAILED(ec_type) if (!ok) { \ #define BREAK_IF_QUERY_FAILED(ec_type) if (!ok) { \
ec = ec_type; \ ec = ec_type; \
MYQCRITICAL << ecStringWithCategory(ec) << "\n" << query.lastError(); \ MYQCRITICAL << ecStringWithCategory(ec) << "\n" << query.lastError(); \
break; \ break; \
} }
enum class DatabaseErrorCode { enum class DatabaseErrorCode {
OpenDbFailed = 1, OpenDbFailed = 1,
CreateDbTableFailed, CreateDbTableFailed,
QueryDbFailed, QueryDbFailed,
}; };
JLIBQT_NAMESPACE_END JLIBQT_NAMESPACE_END
#define ENABLE_ENUM_AS_ERROR_CODE(type) \ #define ENABLE_ENUM_AS_ERROR_CODE(type) \
namespace std { \ namespace std { \
template <> struct is_error_code_enum<type> : true_type {}; \ template <> struct is_error_code_enum<type> : true_type {}; \
std::error_code make_error_code(type); \ std::error_code make_error_code(type); \
} }
ENABLE_ENUM_AS_ERROR_CODE(JLIBQT_NAMESPACE DatabaseErrorCode) ENABLE_ENUM_AS_ERROR_CODE(JLIBQT_NAMESPACE DatabaseErrorCode)
std::error_code std::make_error_code(JLIBQT_NAMESPACE DatabaseErrorCode ec); std::error_code std::make_error_code(JLIBQT_NAMESPACE DatabaseErrorCode ec);
#pragma once #pragma once
#include <QDebug> #include <QDebug>
#include <QDateTime> #include <QDateTime>
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <Windows.h>
#endif //_WIN32 #endif //_WIN32
#define JLIBQT_QDEBUG_FILE_LINE_STREAM "[" << __FUNCTION__ << __LINE__ << "]: " #define JLIBQT_QDEBUG_FILE_LINE_STREAM "[" << __FUNCTION__ << __LINE__ << "]: "
#define JLIBQT_QDEBUG_FILE_LINE_VALUE (QString("[") + __FUNCTION__ + " " + QString::number(__LINE__) + "]: ") #define JLIBQT_QDEBUG_FILE_LINE_VALUE (QString("[") + __FUNCTION__ + " " + QString::number(__LINE__) + "]: ")
#define MYQDEBUG qDebug() << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQDEBUG qDebug() << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQINFO qInfo() << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQINFO qInfo() << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQWARN qWarning() << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQWARN qWarning() << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQCRITICAL qCritical() << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQCRITICAL qCritical() << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQDEBUG_NOQUOTE qDebug().noquote() << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQDEBUG_NOQUOTE qDebug().noquote() << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQINFO_NOQUOTE qInfo().noquote() << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQINFO_NOQUOTE qInfo().noquote() << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQWARN_NOQUOTE qWarning().noquote() << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQWARN_NOQUOTE qWarning().noquote() << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQCRITICAL_NOQUOTE qCritical().noquote() << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQCRITICAL_NOQUOTE qCritical().noquote() << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQDEBUG2 qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQDEBUG2 qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQINFO2 qInfo() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQINFO2 qInfo() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQWARN2 qWarning() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQWARN2 qWarning() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQCRITICAL2 qCritical() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQCRITICAL2 qCritical() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQDEBUG2_NOQUOTE qDebug().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQDEBUG2_NOQUOTE qDebug().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQINFO2_NOQUOTE qInfo().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQINFO2_NOQUOTE qInfo().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQWARN2_NOQUOTE qWarning().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQWARN2_NOQUOTE qWarning().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM
#define MYQCRITICAL2_NOQUOTE qCritical().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM #define MYQCRITICAL2_NOQUOTE qCritical().noquote() << QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss") << JLIBQT_QDEBUG_FILE_LINE_STREAM
//! 弹窗报告行号开关 //! 弹窗报告行号开关
// #define JLIBQT_SHOW_LINE 0 // #define JLIBQT_SHOW_LINE 0
//! 当行号大于下方定义的值时,弹窗报告行号,否则忽略。可在多个cpp文件分别定义不同的值。 //! 当行号大于下方定义的值时,弹窗报告行号,否则忽略。可在多个cpp文件分别定义不同的值。
// #define JLIBQT_SHOW_MSGBOX_AFTER_LINE 1 // #define JLIBQT_SHOW_MSGBOX_AFTER_LINE 1
#if JLIBQT_SHOW_LINE #if JLIBQT_SHOW_LINE
#ifdef JLIBQT_SHOW_MSGBOX_AFTER_LINE #ifdef JLIBQT_SHOW_MSGBOX_AFTER_LINE
#define JLIBQT_SHOW_MSGBOX_LINE_NUMBER if(__LINE__ >= JLIBQT_SHOW_MSGBOX_AFTER_LINE) { MessageBoxW(nullptr, (std::wstring(__FILEW__) + L":" + std::to_wstring(__LINE__)).data(), L"Test", 0); } #define JLIBQT_SHOW_MSGBOX_LINE_NUMBER if(__LINE__ >= JLIBQT_SHOW_MSGBOX_AFTER_LINE) { MessageBoxW(nullptr, (std::wstring(__FILEW__) + L":" + std::to_wstring(__LINE__)).data(), L"Test", 0); }
#define JLIBQT_SHOW_MSGBOX_LINE_NUMBER_WITH_TITLE(title) if(__LINE__ >= JLIBQT_SHOW_MSGBOX_AFTER_LINE) { MessageBoxW(nullptr, (std::wstring(__FILEW__) + L":" + std::to_wstring(__LINE__)).data(), title, 0); } #define JLIBQT_SHOW_MSGBOX_LINE_NUMBER_WITH_TITLE(title) if(__LINE__ >= JLIBQT_SHOW_MSGBOX_AFTER_LINE) { MessageBoxW(nullptr, (std::wstring(__FILEW__) + L":" + std::to_wstring(__LINE__)).data(), title, 0); }
#else #else
#define JLIBQT_SHOW_MSGBOX_LINE_NUMBER { MessageBoxW(nullptr, (std::wstring(__FILEW__) + L":" + std::to_wstring(__LINE__)).data(), L"Test", 0); } #define JLIBQT_SHOW_MSGBOX_LINE_NUMBER { MessageBoxW(nullptr, (std::wstring(__FILEW__) + L":" + std::to_wstring(__LINE__)).data(), L"Test", 0); }
#define JLIBQT_SHOW_MSGBOX_LINE_NUMBER_WITH_TITLE(title) { MessageBoxW(nullptr, (std::wstring(__FILEW__) + L":" + std::to_wstring(__LINE__)).data(), title, 0); } #define JLIBQT_SHOW_MSGBOX_LINE_NUMBER_WITH_TITLE(title) { MessageBoxW(nullptr, (std::wstring(__FILEW__) + L":" + std::to_wstring(__LINE__)).data(), title, 0); }
#endif #endif
#else #else
#define JLIBQT_SHOW_MSGBOX_LINE_NUMBER #define JLIBQT_SHOW_MSGBOX_LINE_NUMBER
#define JLIBQT_SHOW_MSGBOX_LINE_NUMBER_WITH_TITLE(title) #define JLIBQT_SHOW_MSGBOX_LINE_NUMBER_WITH_TITLE(title)
#endif #endif
#pragma once #pragma once
#include "qt_global.h" #include "qt_global.h"
#include <QDir> #include <QDir>
#include <QCoreApplication> #include <QCoreApplication>
#include <QString> #include <QString>
#include <QDateTime> #include <QDateTime>
#include <QStandardPaths> #include <QStandardPaths>
JLIBQT_NAMESPACE_BEGIN JLIBQT_NAMESPACE_BEGIN
static const auto JLIBQT_FILE_TIME_FORMAT = "yyyy-MM-dd_hh-mm-ss"; static const auto JLIBQT_FILE_TIME_FORMAT = "yyyy-MM-dd_hh-mm-ss";
struct PathHelper struct PathHelper
{ {
static inline bool mkpath(const QString& path) { static inline bool mkpath(const QString& path) {
QDir dir; return dir.exists(path) ? true : dir.mkpath(path); QDir dir; return dir.exists(path) ? true : dir.mkpath(path);
} }
static inline QString currentTimeString() { static inline QString currentTimeString() {
return QDateTime::currentDateTime().toString(JLIBQT_FILE_TIME_FORMAT); return QDateTime::currentDateTime().toString(JLIBQT_FILE_TIME_FORMAT);
} }
static inline QString getFolder(const QString& path) { static inline QString getFolder(const QString& path) {
QDir dir(path); dir.cdUp(); QDir dir(path); dir.cdUp();
return dir.absolutePath(); return dir.absolutePath();
} }
virtual ~PathHelper() {} virtual ~PathHelper() {}
virtual QString program() const { return programPath_; } virtual QString program() const { return programPath_; }
virtual QString exe() const { return exePath_; } virtual QString exe() const { return exePath_; }
virtual QString bin() const { return program() + "/bin"; } virtual QString bin() const { return program() + "/bin"; }
virtual QString data() const { return program() + "/data"; } virtual QString data() const { return program() + "/data"; }
virtual QString log() const { return program() + "/log"; } virtual QString log() const { return program() + "/log"; }
virtual QString dumps() const { return program() + "/dumps"; } virtual QString dumps() const { return program() + "/dumps"; }
virtual QString config() const { return data() + "/config"; } virtual QString config() const { return data() + "/config"; }
virtual QString db() const { return data() + "/db"; } virtual QString db() const { return data() + "/db"; }
protected: protected:
// disable constructor // disable constructor
explicit PathHelper() {} explicit PathHelper() {}
//! 应用程序安装目录 //! 应用程序安装目录
QString programPath_ = {}; QString programPath_ = {};
//! 应用程序路径 //! 应用程序路径
QString exePath_ = {}; QString exePath_ = {};
}; };
struct AutoSwithToBin { struct AutoSwithToBin {
AutoSwithToBin(PathHelper* helper) : helper(helper) {} AutoSwithToBin(PathHelper* helper) : helper(helper) {}
AutoSwithToBin(PathHelper* helper, const QString& path) : helper(helper) { AutoSwithToBin(PathHelper* helper, const QString& path) : helper(helper) {
QDir().setCurrent(path); QDir().setCurrent(path);
} }
~AutoSwithToBin() { ~AutoSwithToBin() {
if (helper) { QDir().setCurrent(helper->bin()); } if (helper) { QDir().setCurrent(helper->bin()); }
} }
PathHelper* helper = nullptr; PathHelper* helper = nullptr;
}; };
/* /*
* @brief 路径辅助类,数据保存在安装目录,程序和dll在bin文件夹 * @brief 路径辅助类,数据保存在安装目录,程序和dll在bin文件夹
* @note 大致结构树为: * @note 大致结构树为:
* @note |-- program-install-dir * @note |-- program-install-dir
* @note | |-- bin * @note | |-- bin
* @note | | |-- program.exe * @note | | |-- program.exe
* @note | | |-- *.dlls * @note | | |-- *.dlls
* @note | |-- dumps * @note | |-- dumps
* @note | |-- log * @note | |-- log
* @note | |-- data * @note | |-- data
* @note | |-- config * @note | |-- config
* @note | |-- db * @note | |-- db
*/ */
struct PathHelperLocal : PathHelper struct PathHelperLocal : PathHelper
{ {
PathHelperLocal() : PathHelper() { PathHelperLocal() : PathHelper() {
exePath_ = QCoreApplication::applicationFilePath(); exePath_ = QCoreApplication::applicationFilePath();
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
dir.cdUp(); programPath_ = dir.absolutePath(); dir.cdUp(); programPath_ = dir.absolutePath();
} }
}; };
/* /*
* @brief 路径辅助类,数据保存在安装目录,程序和dll也在安装目录 * @brief 路径辅助类,数据保存在安装目录,程序和dll也在安装目录
* @note 大致结构树为: * @note 大致结构树为:
* @note |-- program-install-dir * @note |-- program-install-dir
* @note | |-- dumps * @note | |-- dumps
* @note | |-- log * @note | |-- log
* @note | |-- data * @note | |-- data
* @note | | |-- config * @note | | |-- config
* @note | | |-- db * @note | | |-- db
* @note | |-- program.exe * @note | |-- program.exe
* @note | |-- *.dlls * @note | |-- *.dlls
*/ */
struct PathHelperLocalWithoutBin : PathHelper struct PathHelperLocalWithoutBin : PathHelper
{ {
PathHelperLocalWithoutBin() : PathHelper() { PathHelperLocalWithoutBin() : PathHelper() {
exePath_ = QCoreApplication::applicationFilePath(); exePath_ = QCoreApplication::applicationFilePath();
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
programPath_ = dir.absolutePath(); programPath_ = dir.absolutePath();
} }
virtual QString bin() const override { return program(); } virtual QString bin() const override { return program(); }
}; };
/* /*
* @brief 路径辅助类,数据保存在其他可写目录,如 C:/Users/[USER]/AppData/Roaming * @brief 路径辅助类,数据保存在其他可写目录,如 C:/Users/[USER]/AppData/Roaming
* @note program-name 无需设置,Qt会自动设置 * @note program-name 无需设置,Qt会自动设置
* @note 如果在调用此类之前先调用了 QCoreApplication::setOrganizationName("your-organization-name"); * @note 如果在调用此类之前先调用了 QCoreApplication::setOrganizationName("your-organization-name");
* @note 那么program-name在your-organization-name下 * @note 那么program-name在your-organization-name下
* @note 大致结构树为: * @note 大致结构树为:
* @note |-- program-install-dir * @note |-- program-install-dir
* @note | |-- bin * @note | |-- bin
* @note | | |-- program.exe * @note | | |-- program.exe
* @note | | |-- *.dlls * @note | | |-- *.dlls
* @note * @note
* @note |-- writable-dir * @note |-- writable-dir
* @note | |-- your-organization-name * @note | |-- your-organization-name
* @note | | |-- program-name * @note | | |-- program-name
* @note | | |-- log * @note | | |-- log
* @note | | |-- dumps * @note | | |-- dumps
* @note | | |-- config * @note | | |-- config
* @note | | |-- db * @note | | |-- db
*/ */
struct PathHelperDataSeperated : PathHelperLocal struct PathHelperDataSeperated : PathHelperLocal
{ {
PathHelperDataSeperated(bool useTmpAsLogFolder = false) PathHelperDataSeperated(bool useTmpAsLogFolder = false)
: PathHelperLocal(), useTmpAsLogFolder(useTmpAsLogFolder) : PathHelperLocal(), useTmpAsLogFolder(useTmpAsLogFolder)
{} {}
virtual QString data() const override { virtual QString data() const override {
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
} }
virtual QString log() const override { return useTmpAsLogFolder ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) : (data() + "/log"); } virtual QString log() const override { return useTmpAsLogFolder ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) : (data() + "/log"); }
virtual QString dumps() const override { return data() + "/dumps"; } virtual QString dumps() const override { return data() + "/dumps"; }
virtual QString config() const override { return data() + "/config"; } virtual QString config() const override { return data() + "/config"; }
virtual QString db() const override { return data() + "/db"; } virtual QString db() const override { return data() + "/db"; }
protected: protected:
bool useTmpAsLogFolder = false; bool useTmpAsLogFolder = false;
}; };
/* /*
* @brief 路径辅助类,数据保存在其他可写目录,如 C:/Users/[USER]/AppData/Roaming * @brief 路径辅助类,数据保存在其他可写目录,如 C:/Users/[USER]/AppData/Roaming
* @note program-name 无需设置,Qt会自动设置 * @note program-name 无需设置,Qt会自动设置
* @note 如果在调用此类之前先调用了 QCoreApplication::setOrganizationName("your-organization-name"); * @note 如果在调用此类之前先调用了 QCoreApplication::setOrganizationName("your-organization-name");
* @note 那么program-name在your-organization-name下 * @note 那么program-name在your-organization-name下
* @note 大致结构树为: * @note 大致结构树为:
* @note |-- program-install-dir * @note |-- program-install-dir
* @note | |-- program.exe * @note | |-- program.exe
* @note | |-- *.dlls * @note | |-- *.dlls
* @note * @note
* @note |-- writable-dir * @note |-- writable-dir
* @note | |-- your-organization-name * @note | |-- your-organization-name
* @note | | |-- program-name * @note | | |-- program-name
* @note | | |-- log * @note | | |-- log
* @note | | |-- dumps * @note | | |-- dumps
* @note | | |-- config * @note | | |-- config
* @note | | |-- db * @note | | |-- db
*/ */
struct PathHelperDataSeperatedWithoutBin : PathHelperLocalWithoutBin struct PathHelperDataSeperatedWithoutBin : PathHelperLocalWithoutBin
{ {
PathHelperDataSeperatedWithoutBin(bool useTmpAsLogFolder = false) PathHelperDataSeperatedWithoutBin(bool useTmpAsLogFolder = false)
: PathHelperLocalWithoutBin(), useTmpAsLogFolder(useTmpAsLogFolder) : PathHelperLocalWithoutBin(), useTmpAsLogFolder(useTmpAsLogFolder)
{} {}
virtual QString data() const override { virtual QString data() const override {
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
} }
virtual QString log() const override { return useTmpAsLogFolder ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) : (data() + "/log"); } virtual QString log() const override { return useTmpAsLogFolder ? QStandardPaths::writableLocation(QStandardPaths::TempLocation) : (data() + "/log"); }
virtual QString dumps() const override { return data() + "/dumps"; } virtual QString dumps() const override { return data() + "/dumps"; }
virtual QString config() const override { return data() + "/config"; } virtual QString config() const override { return data() + "/config"; }
virtual QString db() const override { return data() + "/db"; } virtual QString db() const override { return data() + "/db"; }
protected: protected:
bool useTmpAsLogFolder = false; bool useTmpAsLogFolder = false;
}; };
JLIBQT_NAMESPACE_END JLIBQT_NAMESPACE_END
#pragma once #pragma once
#include "../base/config.h" #include "../base/config.h"
#include <string> #include <string>
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include <vector> #include <vector>
#include <stdint.h> #include <stdint.h>
namespace jlib namespace jlib
{ {
//! 应用程序版本分支 //! 应用程序版本分支
enum class Branch : int { enum class Branch : int {
//! 测试版,一般仅用于测试人员测试 // 开发版,开发人员自测
Test, Dev,
//! 体验版,可以投放给少量用户进行灰度测试 //! 测试版,一般仅用于测试人员测试
Experimental, Test,
//! 正式版 //! 体验版,可以投放给少量用户进行灰度测试
Stable, Experimental,
//! 正式版
BranchCount, Stable,
InvalidBranch = 0x0FFFFFFF, BranchCount,
};
InvalidBranch = 0x0FFFFFFF,
static constexpr auto BranchNameTest = "test"; };
static constexpr auto BranchNameExperimental = "experimental";
static constexpr auto BranchNameStable = "stable"; static constexpr auto BranchNameDev = "dev";
static constexpr auto BranchNameTest = "test";
inline const char* branchName(Branch branch) { static constexpr auto BranchNameExperimental = "experimental";
switch (branch) { static constexpr auto BranchNameStable = "stable";
case jlib::Branch::Test: return BranchNameTest;
case jlib::Branch::Experimental: return BranchNameExperimental; inline const char* branchName(Branch branch) {
case jlib::Branch::Stable: return BranchNameStable; switch (branch) {
default: return "InvalidBranch"; case jlib::Branch::Dev: return BranchNameDev;
} case jlib::Branch::Test: return BranchNameTest;
} case jlib::Branch::Experimental: return BranchNameExperimental;
case jlib::Branch::Stable: return BranchNameStable;
inline Branch branchFromString(const std::string& name) { default: return "InvalidBranch";
if (name == BranchNameTest) { return Branch::Test; } }
else if (name == BranchNameExperimental) { return Branch::Experimental; } }
else if (name == BranchNameStable) { return Branch::Stable; }
else { return Branch::InvalidBranch; } inline Branch branchFromString(const std::string& name) {
} if (name == BranchNameDev) { return Branch::Dev; }
else if (name == BranchNameTest) { return Branch::Test; }
//! 应用程序版本号 else if (name == BranchNameExperimental) { return Branch::Experimental; }
struct Version { else if (name == BranchNameStable) { return Branch::Stable; }
//! 主版本 else { return Branch::InvalidBranch; }
uint16_t major = 0; }
//! 功能更新
uint16_t minor = 0; //! 应用程序版本号
//! bug 修复 struct Version {
uint16_t revision = 0; //! 主版本
//! 随意,我一般用来表示程序总迭代(编译成功)次数 uint16_t major = 0;
uint16_t build = 0; //! 功能更新
uint16_t minor = 0;
Version() = default; //! bug 修复
Version(uint16_t major, uint16_t minor, uint16_t revision, uint16_t build) uint16_t revision = 0;
: major(major), minor(minor), revision(revision), build(build) {} //! 随意,我一般用来表示程序总迭代(编译成功)次数
Version(const std::string& s) { _fromString(s); } uint16_t build = 0;
Version(uint64_t v) { fromUInt64(v); }
Version& fromString(const std::string& s) { _fromString(s); return *this; } Version() = default;
Version& operator=(const std::string& s) { _fromString(s); return *this; } Version(uint16_t major, uint16_t minor, uint16_t revision, uint16_t build)
Version& operator=(uint64_t v) { fromUInt64(v); return *this; } : major(major), minor(minor), revision(revision), build(build) {}
Version(const std::string& s) { _fromString(s); }
bool valid() const { return !(major == 0 && minor == 0 && revision == 0 && build == 0); } Version(uint64_t v) { fromUInt64(v); }
void reset() { major = minor = revision = build = 0; } Version& fromString(const std::string& s) { _fromString(s); return *this; }
Version& operator=(const std::string& s) { _fromString(s); return *this; }
bool _fromString(const std::string& s) { Version& operator=(uint64_t v) { fromUInt64(v); return *this; }
if (std::sscanf(s.c_str(), "%hu.%hu.%hu.%hu", &major, &minor, &revision, &build) != 4) {
reset(); return false; bool valid() const { return !(major == 0 && minor == 0 && revision == 0 && build == 0); }
} void reset() { major = minor = revision = build = 0; }
if (major < 0) major = 0; if (minor < 0) minor = 0;
if (revision < 0) revision = 0; if (build < 0) build = 0; bool _fromString(const std::string& s) {
return true; if (std::sscanf(s.c_str(), "%hu.%hu.%hu.%hu", &major, &minor, &revision, &build) != 4) {
} reset(); return false;
}
std::string toString() const { if (major < 0) major = 0; if (minor < 0) minor = 0;
return std::to_string(major) + "." + if (revision < 0) revision = 0; if (build < 0) build = 0;
std::to_string(minor) + "." + return true;
std::to_string(revision) + "." + }
std::to_string(build);
} std::string toString() const {
return std::to_string(major) + "." +
void fromUInt64(uint64_t v) { std::to_string(minor) + "." +
major = (v >> 48) & 0xFFFF; std::to_string(revision) + "." +
minor = (v >> 32) & 0xFFFF; std::to_string(build);
revision = (v >> 16) & 0xFFFF; }
build = v & 0xFFFF;
} void fromUInt64(uint64_t v) {
major = (v >> 48) & 0xFFFF;
uint64_t toUInt64() const { minor = (v >> 32) & 0xFFFF;
return ((uint64_t)major << 48) | ((uint64_t)minor << 32) | ((uint64_t)revision << 16) | (uint64_t)build; revision = (v >> 16) & 0xFFFF;
} build = v & 0xFFFF;
}
//! require cpp17
template <typename T> uint64_t toUInt64() const {
bool fromFile(T const & path) { return ((uint64_t)major << 48) | ((uint64_t)minor << 32) | ((uint64_t)revision << 16) | (uint64_t)build;
if constexpr (std::is_convertible_v<T, std::string>); }
else if constexpr (std::is_convertible_v<T, std::wstring>);
else { return false; } //! require cpp17
std::ifstream in(path); if (!in) return false; template <typename T>
std::stringstream is; is << in.rdbuf(); bool fromFile(T const & path) {
Version file_ver(is.str()); if constexpr (std::is_convertible_v<T, std::string>);
if (file_ver.valid()) { *this = file_ver; return true; } else if constexpr (std::is_convertible_v<T, std::wstring>);
return false; else { return false; }
} std::ifstream in(path); if (!in) return false;
std::stringstream is; is << in.rdbuf();
//! require cpp17 Version file_ver(is.str());
template <typename T> if (file_ver.valid()) { *this = file_ver; return true; }
bool toFile(T const& path) { return false;
if constexpr (std::is_convertible_v<T, std::string>); }
else if constexpr (std::is_convertible_v<T, std::wstring>);
else { return false; } //! require cpp17
std::ofstream out(path); if (!out) { return false; } template <typename T>
auto str = toString(); out.write(str.data(), str.size()); bool toFile(T const& path) {
return true; if constexpr (std::is_convertible_v<T, std::string>);
} else if constexpr (std::is_convertible_v<T, std::wstring>);
else { return false; }
bool operator == (const Version& ver) const { std::ofstream out(path); if (!out) { return false; }
return toUInt64() == ver.toUInt64(); auto str = toString(); out.write(str.data(), str.size());
} return true;
}
bool operator < (const Version& ver) const {
return toUInt64() < ver.toUInt64(); bool operator == (const Version& ver) const {
} return toUInt64() == ver.toUInt64();
}
bool operator > (const Version& ver) const {
return toUInt64() > ver.toUInt64(); bool operator < (const Version& ver) const {
} return toUInt64() < ver.toUInt64();
}
bool operator <= (const Version& ver) const {
return toUInt64() <= ver.toUInt64(); bool operator > (const Version& ver) const {
} return toUInt64() > ver.toUInt64();
}
bool operator >= (const Version& ver) const {
return toUInt64() >= ver.toUInt64(); bool operator <= (const Version& ver) const {
} return toUInt64() <= ver.toUInt64();
}; }
//! 安装包下载链接 bool operator >= (const Version& ver) const {
struct DownLoadLink { return toUInt64() >= ver.toUInt64();
std::string host = {}; // local, qcloud, aliyun, ... }
std::string link = {}; };
std::string md5 = {};
}; //! 安装包下载链接
struct DownLoadLink {
//! 发布 std::string host = {}; // local, qcloud, aliyun, ...
struct Release { std::string link = {};
Branch branch = Branch::InvalidBranch; std::string md5 = {};
Version version = {}; };
bool force_update = false;
//! 版本更新说明 //! 发布
std::string change = {}; struct Release {
std::vector<DownLoadLink> dllinks = {}; Branch branch = Branch::InvalidBranch;
Version version = {};
std::string toString() const { bool force_update = false;
auto s = std::string(branchName(branch)) + " " + version.toString() + //! 版本更新说明
"\nforce_update=" + (force_update ? "true" : "false") + std::string change = {};
"\nchange=" + change + std::vector<DownLoadLink> dllinks = {};
"\ndownload_links=\n";
if (!dllinks.empty()) { std::string toString() const {
for (const auto& link : dllinks) { s += link.host + " : link=" + link.link + " md5=" + link.md5 + "\n"; } auto s = std::string(branchName(branch)) + " " + version.toString() +
} else { s += "empty"; } "\nforce_update=" + (force_update ? "true" : "false") +
return s; "\nchange=" + change +
} "\ndownload_links=\n";
if (!dllinks.empty()) {
bool valid() const { for (const auto& link : dllinks) { s += link.host + " : link=" + link.link + " md5=" + link.md5 + "\n"; }
return (branch != Branch::InvalidBranch) && version.valid() && !dllinks.empty(); } else { s += "empty"; }
} return s;
}; }
//! 最新的发布,包含各个分支的最新版本 bool valid() const {
struct LatestRelease { return (branch != Branch::InvalidBranch) && version.valid() && !dllinks.empty();
std::vector<Release> releases = {}; }
};
std::string toString() const {
std::string str; //! 最新的发布,包含各个分支的最新版本
for (const auto& r : releases) { struct LatestRelease {
if (r.valid()) { str += r.toString() + "\n"; } std::vector<Release> releases = {};
}
return str; std::string toString() const {
} std::string str;
}; for (const auto& r : releases) {
if (r.valid()) { str += r.toString() + "\n"; }
/* }
* @brief 从服务器获取最新版本信息,包含各个分支的最新版本 return str;
* @param value 为从服务器获取的最新发布信息,以 JSON 表示 }
* @param latestReleases 版本信息 };
* @return count
* @note 示例数据 /*
* @code{.json} * @brief 从服务器获取最新版本信息,包含各个分支的最新版本
* { * @param value 为从服务器获取的最新发布信息,以 JSON 表示
* "test": { * @param latestReleases 版本信息
* "version": "2.2.11.13262", * @return count
* "force_update": true, * @note 示例数据
* "release_note": "测试;", * @code{.json}
* "download_links": { * {
* "local": { * "test": {
* "link": "https://your-host.com/download/installer.exe", * "version": "2.2.11.13262",
* "md5": "06907d59d7a656be62fc93bbf867604b" * "force_update": true,
* } * "release_note": "测试;",
* } * "download_links": {
* }, * "local": {
* "stable": { * "link": "https://your-host.com/download/installer.exe",
* "version": "2.2.12.13282", * "md5": "06907d59d7a656be62fc93bbf867604b"
* "force_update": false, * }
* "release_note": "测试稳定", * }
* "download_links": { * },
* "local": { * "stable": {
* "link": "https://your-host.com/download/installer.exe", * "version": "2.2.12.13282",
* "md5": "06907d59d7a656be62fc93bbf867604b" * "force_update": false,
* }, * "release_note": "测试稳定",
* "qcloud": { * "download_links": {
* "link": "https://xxx.file.myqcloud.com/xxx/installer.exe", * "local": {
* "md5": "06907d59d7a656be62fc93bbf867604b" * "link": "https://your-host.com/download/installer.exe",
* } * "md5": "06907d59d7a656be62fc93bbf867604b"
* } * },
* } * "qcloud": {
* } * "link": "https://xxx.file.myqcloud.com/xxx/installer.exe",
* @endcode * "md5": "06907d59d7a656be62fc93bbf867604b"
*/ * }
template <typename JsonValue> * }
int resolveLatestRelease(const JsonValue& value, LatestRelease& latestRelease) { * }
int count = 0; * }
for (int i = 0; i < (int)Branch::BranchCount; i++) { * @endcode
Branch branch = Branch(i); */
if (value.isMember(branchName(branch))) { template <typename JsonValue>
const auto& detail = value[branchName(branch)]; int resolveLatestRelease(const JsonValue& value, LatestRelease& latestRelease) {
Release release; int count = 0;
release.branch = branch; for (int i = 0; i < (int)Branch::BranchCount; i++) {
release.force_update = detail.isMember("force_update") ? detail["force_update"].asBool() : false; Branch branch = Branch(i);
release.version = detail["version"].asString(); if (value.isMember(branchName(branch))) {
release.change = detail["release_note"].asString(); const auto& detail = value[branchName(branch)];
if (detail["download_links"].isMember("local")) { Release release;
DownLoadLink dl; release.branch = branch;
dl.host = "local"; release.force_update = detail.isMember("force_update") ? detail["force_update"].asBool() : false;
dl.link = detail["download_links"]["local"]["link"].asString(); release.version = detail["version"].asString();
dl.md5 = detail["download_links"]["local"]["md5"].asString(); release.change = detail["release_note"].asString();
release.dllinks.push_back(dl); if (detail["download_links"].isMember("local")) {
} DownLoadLink dl;
if (detail["download_links"].isMember("qcloud")) { dl.host = "local";
DownLoadLink dl; dl.link = detail["download_links"]["local"]["link"].asString();
dl.host = "qcloud"; dl.md5 = detail["download_links"]["local"]["md5"].asString();
dl.link = detail["download_links"]["qcloud"]["link"].asString(); release.dllinks.push_back(dl);
dl.md5 = detail["download_links"]["qcloud"]["md5"].asString(); }
release.dllinks.push_back(dl); if (detail["download_links"].isMember("qcloud")) {
} DownLoadLink dl;
latestRelease.releases.push_back(release); dl.host = "qcloud";
count++; dl.link = detail["download_links"]["qcloud"]["link"].asString();
} dl.md5 = detail["download_links"]["qcloud"]["md5"].asString();
} release.dllinks.push_back(dl);
}
return count; latestRelease.releases.push_back(release);
} count++;
}
} // end namespace jlib }
return count;
}
} // end namespace jlib
#pragma once #pragma once
#include "../base/config.h" #include "../base/config.h"
#include <string> #include <string>
#include "path_op.h" #include "path_op.h"
#include "../util/chrono_wrapper.h" #include "../util/chrono_wrapper.h"
namespace jlib { namespace jlib {
namespace win32 { namespace win32 {
struct PathHelper struct PathHelper
{ {
static inline bool mkpath(const std::string& path) { static inline bool mkpath(const std::string& path) {
return folderExists(path) ? true : createDirectory(path); return folderExists(path) ? true : createDirectory(path);
} }
static inline bool mkpath(const std::wstring& path) { static inline bool mkpath(const std::wstring& path) {
return folderExists(path) ? true : createDirectory(path); return folderExists(path) ? true : createDirectory(path);
} }
static inline std::wstring currentTimeString() { static inline std::wstring currentTimeString() {
return integrateFileName(nowToWString()); return integrateFileName(nowToWString());
} }
static inline std::string currentTimeStringA() { static inline std::string currentTimeStringA() {
return integrateFileName(nowToString()); return integrateFileName(nowToString());
} }
virtual ~PathHelper() {} virtual ~PathHelper() {}
virtual std::string program() const { return programPath_; } virtual std::string program() const { return programPath_; }
virtual std::string exe() const { return exePath_; } virtual std::string exe() const { return exePath_; }
virtual std::string bin() const { return program() + "\\bin"; } virtual std::string bin() const { return program() + "\\bin"; }
virtual std::string data() const { return program() + "\\data"; } virtual std::string data() const { return program() + "\\data"; }
virtual std::string log() const { return program() + "\\log"; } virtual std::string log() const { return program() + "\\log"; }
virtual std::string dumps() const { return program() + "\\dumps"; } virtual std::string dumps() const { return program() + "\\dumps"; }
virtual std::string config() const { return data() + "\\config"; } virtual std::string config() const { return data() + "\\config"; }
virtual std::string db() const { return data() + "\\db"; } virtual std::string db() const { return data() + "\\db"; }
protected: protected:
// disable constructor // disable constructor
explicit PathHelper() {} explicit PathHelper() {}
//! 应用程序安装目录 //! 应用程序安装目录
std::string programPath_ = {}; std::string programPath_ = {};
//! 应用程序路径 //! 应用程序路径
std::string exePath_ = {}; std::string exePath_ = {};
}; };
struct AutoSwithToBin { struct AutoSwithToBin {
AutoSwithToBin(PathHelper* helper) : helper(helper) {} AutoSwithToBin(PathHelper* helper) : helper(helper) {}
AutoSwithToBin(PathHelper* helper, const std::string& path) : helper(helper) { AutoSwithToBin(PathHelper* helper, const std::string& path) : helper(helper) {
::SetCurrentDirectoryA(path.c_str()); ::SetCurrentDirectoryA(path.c_str());
} }
~AutoSwithToBin() { ~AutoSwithToBin() {
if (helper) { ::SetCurrentDirectoryA(helper->bin().c_str()); } if (helper) { ::SetCurrentDirectoryA(helper->bin().c_str()); }
} }
PathHelper* helper = nullptr; PathHelper* helper = nullptr;
}; };
/* /*
* @brief 路径辅助类,数据保存在安装目录,程序和dll在bin文件夹 * @brief 路径辅助类,数据保存在安装目录,程序和dll在bin文件夹
* @note 大致结构树为: * @note 大致结构树为:
* @note |-- program-install-dir * @note |-- program-install-dir
* @note | |-- bin * @note | |-- bin
* @note | | |-- program.exe * @note | | |-- program.exe
* @note | | |-- *.dlls * @note | | |-- *.dlls
* @note | |-- dumps * @note | |-- dumps
* @note | |-- log * @note | |-- log
* @note | |-- data * @note | |-- data
* @note | |-- config * @note | |-- config
* @note | |-- db * @note | |-- db
*/ */
struct PathHelperLocal : PathHelper struct PathHelperLocal : PathHelper
{ {
PathHelperLocal() : PathHelper() { PathHelperLocal() : PathHelper() {
exePath_ = getExePathA(); exePath_ = getExePathA();
programPath_ = getFolder(getFolder(exePath_)); programPath_ = getFolder(getFolder(exePath_));
} }
}; };
/* /*
* @brief 路径辅助类,数据保存在安装目录,程序和dll也在安装目录 * @brief 路径辅助类,数据保存在安装目录,程序和dll也在安装目录
* @note 大致结构树为: * @note 大致结构树为:
* @note |-- program-install-dir * @note |-- program-install-dir
* @note | |-- dumps * @note | |-- dumps
* @note | |-- log * @note | |-- log
* @note | |-- data * @note | |-- data
* @note | | |-- config * @note | | |-- config
* @note | | |-- db * @note | | |-- db
* @note | |-- program.exe * @note | |-- program.exe
* @note | |-- *.dlls * @note | |-- *.dlls
*/ */
struct PathHelperLocalWithoutBin : PathHelper struct PathHelperLocalWithoutBin : PathHelper
{ {
PathHelperLocalWithoutBin() : PathHelper() { PathHelperLocalWithoutBin() : PathHelper() {
exePath_ = getExePathA(); exePath_ = getExePathA();
programPath_ = getFolder(exePath_); programPath_ = getFolder(exePath_);
} }
virtual std::string bin() const override { return program(); } virtual std::string bin() const override { return program(); }
}; };
/* /*
* @brief 路径辅助类,数据保存在其他可写目录,如 C:/Users/[USER]/AppData/Roaming * @brief 路径辅助类,数据保存在其他可写目录,如 C:/Users/[USER]/AppData/Roaming
* @note 大致结构树为: * @note 大致结构树为:
* @note |-- program-install-dir * @note |-- program-install-dir
* @note | |-- bin * @note | |-- bin
* @note | | |-- program.exe * @note | | |-- program.exe
* @note | | |-- *.dlls * @note | | |-- *.dlls
* @note * @note
* @note |-- C:/Users/[USER]/AppData/Roaming * @note |-- C:/Users/[USER]/AppData/Roaming
* @note | |-- program-name * @note | |-- program-name
* @note | |-- log * @note | |-- log
* @note | |-- dumps * @note | |-- dumps
* @note | |-- config * @note | |-- config
* @note | |-- db * @note | |-- db
*/ */
struct PathHelperDataSeperated : PathHelperLocal struct PathHelperDataSeperated : PathHelperLocal
{ {
PathHelperDataSeperated(const std::string& appName, bool useTmpAsLogFolder = false) PathHelperDataSeperated(const std::string& appName, bool useTmpAsLogFolder = false)
: PathHelperLocal(), appName(appName), useTmpAsLogFolder(useTmpAsLogFolder) : PathHelperLocal(), appName(appName), useTmpAsLogFolder(useTmpAsLogFolder)
{} {}
virtual std::string data() const override { virtual std::string data() const override {
auto path = getAppDataPathA() + "\\" + appName; auto path = getAppDataPathA() + "\\" + appName;
mkpath(path); return path; mkpath(path); return path;
} }
virtual std::string log() const override { return useTmpAsLogFolder ? getTempPathA() : (data() + "\\log"); } virtual std::string log() const override { return useTmpAsLogFolder ? getTempPathA() : (data() + "\\log"); }
virtual std::string dumps() const override { return data() + "\\dumps"; } virtual std::string dumps() const override { return data() + "\\dumps"; }
virtual std::string config() const override { return data() + "\\config"; } virtual std::string config() const override { return data() + "\\config"; }
virtual std::string db() const override { return data() + "\\db"; } virtual std::string db() const override { return data() + "\\db"; }
protected: protected:
std::string appName = {}; std::string appName = {};
bool useTmpAsLogFolder = false; bool useTmpAsLogFolder = false;
}; };
/* /*
* @brief 路径辅助类,数据保存在其他可写目录,如 C:/Users/[USER]/AppData/Roaming * @brief 路径辅助类,数据保存在其他可写目录,如 C:/Users/[USER]/AppData/Roaming
* @note 大致结构树为: * @note 大致结构树为:
* @note |-- program-install-dir * @note |-- program-install-dir
* @note | |-- program.exe * @note | |-- program.exe
* @note | |-- *.dlls * @note | |-- *.dlls
* @note * @note
* @note |-- C:/Users/[USER]/AppData/Roaming * @note |-- C:/Users/[USER]/AppData/Roaming
* @note | |-- program-name * @note | |-- program-name
* @note | |-- log * @note | |-- log
* @note | |-- dumps * @note | |-- dumps
* @note | |-- config * @note | |-- config
* @note | |-- db * @note | |-- db
*/ */
struct PathHelperDataSeperatedWithoutBin : PathHelperLocalWithoutBin struct PathHelperDataSeperatedWithoutBin : PathHelperLocalWithoutBin
{ {
PathHelperDataSeperatedWithoutBin(const std::string& appName, bool useTmpAsLogFolder = false) PathHelperDataSeperatedWithoutBin(const std::string& appName, bool useTmpAsLogFolder = false)
: PathHelperLocalWithoutBin(), appName(appName), useTmpAsLogFolder(useTmpAsLogFolder) : PathHelperLocalWithoutBin(), appName(appName), useTmpAsLogFolder(useTmpAsLogFolder)
{} {}
virtual std::string data() const override { virtual std::string data() const override {
auto path = getAppDataPathA() + "\\" + appName; auto path = getAppDataPathA() + "\\" + appName;
mkpath(path); return path; mkpath(path); return path;
} }
virtual std::string log() const override { return useTmpAsLogFolder ? getTempPathA() : (data() + "\\log"); } virtual std::string log() const override { return useTmpAsLogFolder ? getTempPathA() : (data() + "\\log"); }
virtual std::string dumps() const override { return data() + "\\dumps"; } virtual std::string dumps() const override { return data() + "\\dumps"; }
virtual std::string config() const override { return data() + "\\config"; } virtual std::string config() const override { return data() + "\\config"; }
virtual std::string db() const override { return data() + "\\db"; } virtual std::string db() const override { return data() + "\\db"; }
protected: protected:
std::string appName = {}; std::string appName = {};
bool useTmpAsLogFolder = false; bool useTmpAsLogFolder = false;
}; };
} }
} }
#pragma once #pragma once
#include "../base/config.h" #include "../base/config.h"
#include <ShlObj.h> #include <ShlObj.h>
#include <string> #include <string>
#include <algorithm> #include <algorithm>
namespace jlib { namespace jlib {
namespace win32 { namespace win32 {
inline std::wstring getExePath() { inline std::wstring getExePath() {
wchar_t path[1024] = { 0 }; ::GetModuleFileNameW(nullptr, path, 1024); return path; wchar_t path[1024] = { 0 }; ::GetModuleFileNameW(nullptr, path, 1024); return path;
} }
inline std::string getExePathA() { inline std::string getExePathA() {
char path[1024] = { 0 }; ::GetModuleFileNameA(nullptr, path, 1024); return path; char path[1024] = { 0 }; ::GetModuleFileNameA(nullptr, path, 1024); return path;
} }
inline std::wstring getFolder(const std::wstring& path) { inline std::wstring getFolder(const std::wstring& path) {
auto pos = path.find_last_of(L"\\/"); return pos != path.npos ? path.substr(0, pos) : path; auto pos = path.find_last_of(L"\\/"); return pos != path.npos ? path.substr(0, pos) : path;
} }
inline std::string getFolder(const std::string& path) { inline std::string getFolder(const std::string& path) {
auto pos = path.find_last_of("\\/"); return pos != path.npos ? path.substr(0, pos) : path; auto pos = path.find_last_of("\\/"); return pos != path.npos ? path.substr(0, pos) : path;
} }
inline std::wstring getExeFolderPath() { return getFolder(getExePath()); } inline std::wstring getExeFolderPath() { return getFolder(getExePath()); }
inline std::string getExeFolderPathA() { return getFolder(getExePathA()); } inline std::string getExeFolderPathA() { return getFolder(getExePathA()); }
static constexpr const wchar_t* DEFAULT_PATH_FILTERW = L"\\/:*?\"<>| "; static constexpr const wchar_t* DEFAULT_PATH_FILTERW = L"\\/:*?\"<>| ";
static constexpr const char* DEFAULT_PATH_FILTER = "\\/:*?\"<>| "; static constexpr const char* DEFAULT_PATH_FILTER = "\\/:*?\"<>| ";
inline std::wstring integrateFileName(const std::wstring& name, const std::wstring& filter = DEFAULT_PATH_FILTERW, wchar_t replace_by = L'_') { inline std::wstring integrateFileName(const std::wstring& name, const std::wstring& filter = DEFAULT_PATH_FILTERW, wchar_t replace_by = L'_') {
auto ret = name; for (auto c : filter) { std::replace(ret.begin(), ret.end(), c, replace_by); } return ret; auto ret = name; for (auto c : filter) { std::replace(ret.begin(), ret.end(), c, replace_by); } return ret;
} }
inline std::string integrateFileName(const std::string& name, const std::string& filter = DEFAULT_PATH_FILTER, char replace_by = '_') { inline std::string integrateFileName(const std::string& name, const std::string& filter = DEFAULT_PATH_FILTER, char replace_by = '_') {
auto ret = name; for (auto c : filter) { std::replace(ret.begin(), ret.end(), c, replace_by); } return ret; auto ret = name; for (auto c : filter) { std::replace(ret.begin(), ret.end(), c, replace_by); } return ret;
} }
inline std::wstring getSpecialFolder(int csidl) { inline std::wstring getSpecialFolder(int csidl) {
wchar_t path[MAX_PATH] = { 0 }; wchar_t path[MAX_PATH] = { 0 };
return ::SHGetSpecialFolderPathW(nullptr, path, csidl, false) ? std::wstring(path) : std::wstring(); return ::SHGetSpecialFolderPathW(nullptr, path, csidl, false) ? std::wstring(path) : std::wstring();
} }
inline std::string getSpecialFolderA(int csidl) { inline std::string getSpecialFolderA(int csidl) {
char path[MAX_PATH] = { 0 }; char path[MAX_PATH] = { 0 };
return ::SHGetSpecialFolderPathA(nullptr, path, csidl, false) ? std::string(path) : std::string(); return ::SHGetSpecialFolderPathA(nullptr, path, csidl, false) ? std::string(path) : std::string();
} }
/** /**
* @brief 获取%APPDATA%路径,一般用于存储程序配置文件 * @brief 获取%APPDATA%路径,一般用于存储程序配置文件
* @note 一般xp的结果为: C:\Documents and Setting\[UserName]\Application Data * @note 一般xp的结果为: C:\Documents and Setting\[UserName]\Application Data
* @note 一般vista及以上的结果为:C:\Users\[UserName]\AppData\Roaming * @note 一般vista及以上的结果为:C:\Users\[UserName]\AppData\Roaming
*/ */
inline std::wstring getAppDataPath() { return getSpecialFolder(CSIDL_APPDATA); } inline std::wstring getAppDataPath() { return getSpecialFolder(CSIDL_APPDATA); }
inline std::string getAppDataPathA() { return getSpecialFolderA(CSIDL_APPDATA); } inline std::string getAppDataPathA() { return getSpecialFolderA(CSIDL_APPDATA); }
inline std::wstring getTempPath() { wchar_t path[MAX_PATH] = { 0 }; ::GetTempPathW(MAX_PATH, path); return path; } inline std::wstring getTempPath() { wchar_t path[MAX_PATH] = { 0 }; ::GetTempPathW(MAX_PATH, path); return path; }
inline std::string getTempPathA() { char path[MAX_PATH] = { 0 }; ::GetTempPathA(MAX_PATH, path); return path; } inline std::string getTempPathA() { char path[MAX_PATH] = { 0 }; ::GetTempPathA(MAX_PATH, path); return path; }
inline std::wstring getMyDocumentsPath() { return getSpecialFolder(CSIDL_MYDOCUMENTS); } inline std::wstring getMyDocumentsPath() { return getSpecialFolder(CSIDL_MYDOCUMENTS); }
inline std::string getMyDocumentsPathA() { return getSpecialFolderA(CSIDL_MYDOCUMENTS); } inline std::string getMyDocumentsPathA() { return getSpecialFolderA(CSIDL_MYDOCUMENTS); }
inline std::wstring getTempFileName(const std::wstring& folder, const std::wstring& pre) { inline std::wstring getTempFileName(const std::wstring& folder, const std::wstring& pre) {
wchar_t path[MAX_PATH] = { 0 }; wchar_t path[MAX_PATH] = { 0 };
UINT ret = ::GetTempFileNameW(folder.c_str(), pre.c_str(), 0, path); UINT ret = ::GetTempFileNameW(folder.c_str(), pre.c_str(), 0, path);
if (ret != 0) { return path; } return std::wstring(); if (ret != 0) { return path; } return std::wstring();
} }
inline std::string getTempFileName(const std::string& folder, const std::string& pre) { inline std::string getTempFileName(const std::string& folder, const std::string& pre) {
char path[MAX_PATH] = { 0 }; char path[MAX_PATH] = { 0 };
UINT ret = ::GetTempFileNameA(folder.c_str(), pre.c_str(), 0, path); UINT ret = ::GetTempFileNameA(folder.c_str(), pre.c_str(), 0, path);
if (ret != 0) { return path; } return std::string(); if (ret != 0) { return path; } return std::string();
} }
inline std::wstring getTempFileName(const std::wstring& pre = L"JLIB") { inline std::wstring getTempFileName(const std::wstring& pre = L"JLIB") {
return getTempFileName(getTempPath(), pre); return getTempFileName(getTempPath(), pre);
} }
inline std::string getTempFileNameA(const std::string& pre = "JLIB") { inline std::string getTempFileNameA(const std::string& pre = "JLIB") {
return getTempFileName(getTempPathA(), pre); return getTempFileName(getTempPathA(), pre);
} }
inline bool folderExists(const std::wstring& folderPath) { inline bool folderExists(const std::wstring& folderPath) {
DWORD dwAttrib = ::GetFileAttributesW(folderPath.data()); DWORD dwAttrib = ::GetFileAttributesW(folderPath.data());
return (dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); return (dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
} }
inline bool folderExists(const std::string& folderPath) { inline bool folderExists(const std::string& folderPath) {
DWORD dwAttrib = ::GetFileAttributesA(folderPath.data()); DWORD dwAttrib = ::GetFileAttributesA(folderPath.data());
return (dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); return (dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
} }
inline bool fileExists(const std::wstring& filePath) { inline bool fileExists(const std::wstring& filePath) {
DWORD dwAttrib = ::GetFileAttributesW(filePath.data()); DWORD dwAttrib = ::GetFileAttributesW(filePath.data());
return (dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY); return (dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
} }
inline bool fileExists(const std::string& filePath) { inline bool fileExists(const std::string& filePath) {
DWORD dwAttrib = ::GetFileAttributesA(filePath.data()); DWORD dwAttrib = ::GetFileAttributesA(filePath.data());
return (dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY); return (dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
} }
inline bool createDirectory(const std::wstring& path) { inline bool createDirectory(const std::wstring& path) {
return ::CreateDirectoryW(path.data(), nullptr) ? true : false; return ::CreateDirectoryW(path.data(), nullptr) ? true : false;
} }
inline bool createDirectory(const std::string& path) { inline bool createDirectory(const std::string& path) {
return ::CreateDirectoryA(path.data(), nullptr) ? true : false; return ::CreateDirectoryA(path.data(), nullptr) ? true : false;
} }
//! 应用程序典型路径辅助 //! 应用程序典型路径辅助
//struct PathHelper { //struct PathHelper {
// //
// //
//}; //};
} }
} }
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
#include <tlhelp32.h> #include <tlhelp32.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <unordered_set> #include <unordered_set>
#include "lasterror.h" #include "lasterror.h"
#include "UnicodeTool.h" #include "UnicodeTool.h"
namespace jlib namespace jlib
{ {
namespace win32 namespace win32
{ {
/** /**
* @brief 启动程序 * @brief 启动程序
* @param path 启动命令 * @param path 启动命令
* @param wait_app_exit 是否阻塞等待程序退出 * @param wait_app_exit 是否阻塞等待程序退出
* @param show 是否显示程序 * @param show 是否显示程序
* @return 启动程序失败返回0xFFFFFFFF;当 wait_app_exit 为真,返回程序的进程返回值;否则返回程序进程ID; * @return 启动程序失败返回0xFFFFFFFF;当 wait_app_exit 为真,返回程序的进程返回值;否则返回程序进程ID;
*/ */
inline DWORD daemon(const std::wstring& path, bool wait_app_exit = true, bool show = true) { inline DWORD daemon(const std::wstring& path, bool wait_app_exit = true, bool show = true) {
STARTUPINFOW si = { 0 }; STARTUPINFOW si = { 0 };
si.cb = sizeof(si); si.cb = sizeof(si);
si.dwFlags |= STARTF_USESHOWWINDOW; si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = show ? SW_SHOW : SW_HIDE; si.wShowWindow = show ? SW_SHOW : SW_HIDE;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
DWORD dwCreationFlags = show ? 0 : CREATE_NO_WINDOW; DWORD dwCreationFlags = show ? 0 : CREATE_NO_WINDOW;
BOOL bRet = CreateProcessW(NULL, (LPWSTR)(path.c_str()), NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &si, &pi); BOOL bRet = CreateProcessW(NULL, (LPWSTR)(path.c_str()), NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &si, &pi);
if (bRet) { if (bRet) {
if (wait_app_exit) { if (wait_app_exit) {
WaitForSingleObject(pi.hProcess, INFINITE); WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExit; DWORD dwExit;
::GetExitCodeProcess(pi.hProcess, &dwExit); ::GetExitCodeProcess(pi.hProcess, &dwExit);
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
CloseHandle(pi.hProcess); CloseHandle(pi.hProcess);
return dwExit; return dwExit;
} else { } else {
return pi.dwProcessId; return pi.dwProcessId;
} }
} else { } else {
auto msg = formatLastError(""); auto msg = formatLastError("");
(void)msg.length(); (void)msg.length();
} }
return 0xFFFFFFFF; return 0xFFFFFFFF;
} }
/** /**
* @brief 启动程序 * @brief 启动程序
* @param path 启动命令 * @param path 启动命令
* @param wait_app_exit 是否阻塞等待程序退出 * @param wait_app_exit 是否阻塞等待程序退出
* @param show 是否显示程序 * @param show 是否显示程序
* @return 启动程序失败返回0xFFFFFFFF;当 wait_app_exit 为真,返回程序的进程返回值;否则返回程序进程ID; * @return 启动程序失败返回0xFFFFFFFF;当 wait_app_exit 为真,返回程序的进程返回值;否则返回程序进程ID;
*/ */
inline DWORD daemon(const std::string& path, bool wait_app_exit = true, bool show = true) { inline DWORD daemon(const std::string& path, bool wait_app_exit = true, bool show = true) {
return daemon(win32::mbcs_to_utf16(path), wait_app_exit, show); return daemon(win32::mbcs_to_utf16(path), wait_app_exit, show);
} }
/** /**
* @brief 获取父进程ID * @brief 获取父进程ID
* @return 成功返回父进程ID,否则返回0 * @return 成功返回父进程ID,否则返回0
*/ */
inline DWORD getppid() inline DWORD getppid()
{ {
HANDLE hSnapshot; HANDLE hSnapshot;
PROCESSENTRY32W pe32; PROCESSENTRY32W pe32;
DWORD ppid = 0, pid = GetCurrentProcessId(); DWORD ppid = 0, pid = GetCurrentProcessId();
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid); hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid);
__try { __try {
if (hSnapshot == INVALID_HANDLE_VALUE) __leave; if (hSnapshot == INVALID_HANDLE_VALUE) __leave;
ZeroMemory(&pe32, sizeof(pe32)); ZeroMemory(&pe32, sizeof(pe32));
pe32.dwSize = sizeof(pe32); pe32.dwSize = sizeof(pe32);
if (!Process32FirstW(hSnapshot, &pe32)) __leave; if (!Process32FirstW(hSnapshot, &pe32)) __leave;
do { do {
if (pe32.th32ProcessID == pid) { if (pe32.th32ProcessID == pid) {
ppid = pe32.th32ParentProcessID; ppid = pe32.th32ParentProcessID;
break; break;
} }
} while (Process32NextW(hSnapshot, &pe32)); } while (Process32NextW(hSnapshot, &pe32));
} __finally { } __finally {
if (hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot); if (hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot);
} }
return ppid; return ppid;
} }
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////
// https://docs.microsoft.com/zh-cn/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes // https://docs.microsoft.com/zh-cn/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes
struct ProcessInfo { struct ProcessInfo {
struct ModuleInfo { struct ModuleInfo {
std::wstring name; std::wstring name;
std::wstring path; std::wstring path;
DWORD pid; DWORD pid;
DWORD base_address; DWORD base_address;
DWORD base_size; DWORD base_size;
}; };
struct ThreadInfo { struct ThreadInfo {
DWORD tid; DWORD tid;
DWORD base_priority; DWORD base_priority;
DWORD delta_priority; DWORD delta_priority;
}; };
std::wstring name; std::wstring name;
std::wstring path; std::wstring path;
DWORD pid; DWORD pid;
DWORD ppid; DWORD ppid;
DWORD thread_count; DWORD thread_count;
DWORD priority_base; DWORD priority_base;
DWORD priority_class; DWORD priority_class;
std::vector<ModuleInfo> modules; std::vector<ModuleInfo> modules;
std::vector<ThreadInfo> threads; std::vector<ThreadInfo> threads;
template <typename JsonValue> template <typename JsonValue>
JsonValue toJson() const { JsonValue toJson() const {
JsonValue v; JsonValue v;
v["name"] = utf16_to_mbcs(name); v["name"] = utf16_to_mbcs(name);
v["path"] = utf16_to_mbcs(path); v["path"] = utf16_to_mbcs(path);
v["pid"] = (size_t)pid; v["pid"] = (size_t)pid;
v["ppid"] = (size_t)ppid; v["ppid"] = (size_t)ppid;
v["thread_count"] = (size_t)thread_count; v["thread_count"] = (size_t)thread_count;
v["priority_base"] = (size_t)priority_base; v["priority_base"] = (size_t)priority_base;
v["priority_class"] = (size_t)priority_class; v["priority_class"] = (size_t)priority_class;
auto& ms = v["modules"]; auto& ms = v["modules"];
for (const auto& m : modules) { for (const auto& m : modules) {
JsonValue jm; JsonValue jm;
jm["name"] = utf16_to_mbcs(m.name); jm["name"] = utf16_to_mbcs(m.name);
jm["path"] = utf16_to_mbcs(m.path); jm["path"] = utf16_to_mbcs(m.path);
jm["pid"] = (size_t)m.pid; jm["pid"] = (size_t)m.pid;
jm["base_address"] = (size_t)m.base_address; jm["base_address"] = (size_t)m.base_address;
jm["base_size"] = (size_t)m.base_size; jm["base_size"] = (size_t)m.base_size;
ms.append(jm); ms.append(jm);
} }
auto& ts = v["threads"]; auto& ts = v["threads"];
for (const auto& t : threads) { for (const auto& t : threads) {
JsonValue jt; JsonValue jt;
jt["tid"] = (size_t)t.tid; jt["tid"] = (size_t)t.tid;
jt["base_priority"] = (size_t)t.base_priority; jt["base_priority"] = (size_t)t.base_priority;
jt["delta_priority"] = (size_t)t.delta_priority; jt["delta_priority"] = (size_t)t.delta_priority;
ts.append(jt); ts.append(jt);
} }
return v; return v;
} }
}; };
typedef std::vector<ProcessInfo> ProcessInfos; typedef std::vector<ProcessInfo> ProcessInfos;
template <typename JsonValue> template <typename JsonValue>
inline JsonValue toJson(const ProcessInfos& pinfos) { inline JsonValue toJson(const ProcessInfos& pinfos) {
JsonValue v; JsonValue v;
for (const auto& pinfo : pinfos) { for (const auto& pinfo : pinfos) {
v.append(pinfo.toJson<JsonValue>()); v.append(pinfo.toJson<JsonValue>());
} }
return v; return v;
} }
typedef std::function<void(const std::wstring&)> ErrorOutputFunc; typedef std::function<void(const std::wstring&)> ErrorOutputFunc;
inline void dummyErrorOutputFunc(const std::wstring& msg) { inline void dummyErrorOutputFunc(const std::wstring& msg) {
printf("%ls\n", msg.data()); printf("%ls\n", msg.data());
} }
inline void outputLastErrorHelper(const std::wstring& msg, ErrorOutputFunc func = nullptr) { inline void outputLastErrorHelper(const std::wstring& msg, ErrorOutputFunc func = nullptr) {
if (!func) { return; } if (!func) { return; }
func(formatLastError(msg)); func(formatLastError(msg));
} }
inline std::vector<ProcessInfo::ModuleInfo> getProcessModules(DWORD dwPID, ErrorOutputFunc output = dummyErrorOutputFunc) inline std::vector<ProcessInfo::ModuleInfo> getProcessModules(DWORD dwPID, ErrorOutputFunc output = dummyErrorOutputFunc)
{ {
std::vector<ProcessInfo::ModuleInfo> modules = {}; std::vector<ProcessInfo::ModuleInfo> modules = {};
HANDLE hModuleSnap = INVALID_HANDLE_VALUE; HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32; MODULEENTRY32 me32;
// Take a snapshot of all modules in the specified process. // Take a snapshot of all modules in the specified process.
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID); hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
if (hModuleSnap == INVALID_HANDLE_VALUE) { if (hModuleSnap == INVALID_HANDLE_VALUE) {
outputLastErrorHelper((L"CreateToolhelp32Snapshot (of modules)"), output); outputLastErrorHelper((L"CreateToolhelp32Snapshot (of modules)"), output);
return(modules); return(modules);
} }
// Set the size of the structure before using it. // Set the size of the structure before using it.
me32.dwSize = sizeof(MODULEENTRY32); me32.dwSize = sizeof(MODULEENTRY32);
// Retrieve information about the first module, // Retrieve information about the first module,
// and exit if unsuccessful // and exit if unsuccessful
if (!Module32First(hModuleSnap, &me32)) { if (!Module32First(hModuleSnap, &me32)) {
outputLastErrorHelper((L"Module32First"), output); // show cause of failure outputLastErrorHelper((L"Module32First"), output); // show cause of failure
CloseHandle(hModuleSnap); // clean the snapshot object CloseHandle(hModuleSnap); // clean the snapshot object
return(modules); return(modules);
} }
// Now walk the module list of the process, // Now walk the module list of the process,
// and display information about each module // and display information about each module
do { do {
ProcessInfo::ModuleInfo info; ProcessInfo::ModuleInfo info;
info.name = me32.szModule; info.name = me32.szModule;
info.path = me32.szExePath; info.path = me32.szExePath;
info.pid = me32.th32ProcessID; info.pid = me32.th32ProcessID;
info.base_address = (DWORD)me32.modBaseAddr; info.base_address = (DWORD)me32.modBaseAddr;
info.base_size = me32.modBaseSize; info.base_size = me32.modBaseSize;
modules.emplace_back(info); modules.emplace_back(info);
} while (Module32Next(hModuleSnap, &me32)); } while (Module32Next(hModuleSnap, &me32));
CloseHandle(hModuleSnap); CloseHandle(hModuleSnap);
return(modules); return(modules);
} }
inline std::vector<ProcessInfo::ThreadInfo> getProcessThreads(DWORD dwOwnerPID, ErrorOutputFunc output = dummyErrorOutputFunc) inline std::vector<ProcessInfo::ThreadInfo> getProcessThreads(DWORD dwOwnerPID, ErrorOutputFunc output = dummyErrorOutputFunc)
{ {
std::vector<ProcessInfo::ThreadInfo> threads = {}; std::vector<ProcessInfo::ThreadInfo> threads = {};
HANDLE hThreadSnap = INVALID_HANDLE_VALUE; HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32; THREADENTRY32 te32;
// Take a snapshot of all running threads // Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwOwnerPID); hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwOwnerPID);
if (hThreadSnap == INVALID_HANDLE_VALUE) if (hThreadSnap == INVALID_HANDLE_VALUE)
return(threads); return(threads);
// Fill in the size of the structure before using it. // Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32); te32.dwSize = sizeof(THREADENTRY32);
// Retrieve information about the first thread, // Retrieve information about the first thread,
// and exit if unsuccessful // and exit if unsuccessful
if (!Thread32First(hThreadSnap, &te32)) { if (!Thread32First(hThreadSnap, &te32)) {
outputLastErrorHelper((L"Thread32First"), output); // show cause of failure outputLastErrorHelper((L"Thread32First"), output); // show cause of failure
CloseHandle(hThreadSnap); // clean the snapshot object CloseHandle(hThreadSnap); // clean the snapshot object
return(threads); return(threads);
} }
// Now walk the thread list of the system, // Now walk the thread list of the system,
// and display information about each thread // and display information about each thread
// associated with the specified process // associated with the specified process
do { do {
if (te32.th32OwnerProcessID == dwOwnerPID) { if (te32.th32OwnerProcessID == dwOwnerPID) {
ProcessInfo::ThreadInfo tinfo; ProcessInfo::ThreadInfo tinfo;
tinfo.tid = te32.th32ThreadID; tinfo.tid = te32.th32ThreadID;
tinfo.base_priority = te32.tpBasePri; tinfo.base_priority = te32.tpBasePri;
tinfo.delta_priority = te32.tpDeltaPri; tinfo.delta_priority = te32.tpDeltaPri;
threads.emplace_back(tinfo); threads.emplace_back(tinfo);
} }
} while (Thread32Next(hThreadSnap, &te32)); } while (Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap); CloseHandle(hThreadSnap);
return(threads); return(threads);
} }
static const wchar_t* PROCESS_FILTER[] = { static const wchar_t* PROCESS_FILTER[] = {
L"[System Process]", L"[System Process]",
L"ApplicationFrameHost.exe", L"ApplicationFrameHost.exe",
L"AppVShNotify.exe", L"AppVShNotify.exe",
L"audiodg.exe", L"audiodg.exe",
L"backgroundTaskHost.exe", L"backgroundTaskHost.exe",
L"ChsIME.exe", L"ChsIME.exe",
L"CompPkgSrv.exe", L"CompPkgSrv.exe",
L"conhost.exe", L"conhost.exe",
L"csrss.exe", L"csrss.exe",
L"ctfmon.exe", L"ctfmon.exe",
L"dasHost.exe", L"dasHost.exe",
L"dllhost.exe", L"dllhost.exe",
L"dwm.exe", L"dwm.exe",
L"fontdrvhost.exe", L"fontdrvhost.exe",
L"GameBarFTServer.exe", L"GameBarFTServer.exe",
L"LockApp.exe", L"LockApp.exe",
L"LogonUI.exe", L"LogonUI.exe",
L"lsass.exe", L"lsass.exe",
L"lsm.exe", L"lsm.exe",
L"Memory Compression", L"Memory Compression",
L"Microsoft.Photos.exe", L"Microsoft.Photos.exe",
L"msdtc.exe", L"msdtc.exe",
L"mstsc.exe", L"mstsc.exe",
L"pacjsworker.exe", L"pacjsworker.exe",
L"PresentationFontCache.exe", L"PresentationFontCache.exe",
L"rdpclip.exe", L"rdpclip.exe",
L"Registry", L"Registry",
L"RemindersServer.exe", L"RemindersServer.exe",
L"rundll32.exe", L"rundll32.exe",
L"RuntimeBroker.exe", L"RuntimeBroker.exe",
L"schtasks.exe", L"schtasks.exe",
L"SearchFilterHost.exe", L"SearchFilterHost.exe",
L"SearchIndexer.exe", L"SearchIndexer.exe",
L"SearchProtocolHost.exe", L"SearchProtocolHost.exe",
L"SearchUI.exe", L"SearchUI.exe",
L"SecurityHealthService.exe", L"SecurityHealthService.exe",
L"SecurityHealthSystray.exe", L"SecurityHealthSystray.exe",
L"services.exe", L"services.exe",
L"SettingSyncHost.exe", L"SettingSyncHost.exe",
L"ShellExperienceHost.exe", L"ShellExperienceHost.exe",
L"sihost.exe", L"sihost.exe",
L"SkypeApp.exe", L"SkypeApp.exe",
L"SkypeBackgroundHost.exe", L"SkypeBackgroundHost.exe",
L"SkypeBridge.exe", L"SkypeBridge.exe",
L"smartscreen.exe", L"smartscreen.exe",
L"smss.exe", L"smss.exe",
L"SgrmBroker.exe", L"SgrmBroker.exe",
L"spoolsv.exe", L"spoolsv.exe",
L"StartMenuExperienceHost.exe", L"StartMenuExperienceHost.exe",
L"svchost.exe", L"svchost.exe",
L"System", L"System",
L"SystemSettingsBroker.exe", L"SystemSettingsBroker.exe",
L"TabTip.exe", L"TabTip.exe",
L"taskhost.exe", L"taskhost.exe",
L"taskhostw.exe", L"taskhostw.exe",
L"TiWorker.exe", L"TiWorker.exe",
L"TrustedInstaller.exe", L"TrustedInstaller.exe",
L"Video.UI.exe", L"Video.UI.exe",
L"WindowsInternal.ComposableShell.Experiences.TextInput.InputApp.exe", L"WindowsInternal.ComposableShell.Experiences.TextInput.InputApp.exe",
L"wininit.exe", L"wininit.exe",
L"winlogon.exe", L"winlogon.exe",
L"WinStore.App.exe", L"WinStore.App.exe",
L"WmiPrvSE.exe", L"WmiPrvSE.exe",
L"wmpnetwk.exe", L"wmpnetwk.exe",
L"WUDFHost.exe", L"WUDFHost.exe",
L"YourPhone.exe", L"YourPhone.exe",
}; };
inline ProcessInfos getProcessesInfo(ErrorOutputFunc output = dummyErrorOutputFunc, bool withModules = false, bool withThreads = false, const wchar_t** filter = PROCESS_FILTER, size_t filter_count = _countof(PROCESS_FILTER)) inline ProcessInfos getProcessesInfo(ErrorOutputFunc output = dummyErrorOutputFunc, bool withModules = false, bool withThreads = false, const wchar_t** filter = PROCESS_FILTER, size_t filter_count = _countof(PROCESS_FILTER))
{ {
// pre-process filter // pre-process filter
std::unordered_set<std::wstring> filterset; std::unordered_set<std::wstring> filterset;
for (size_t i = 0; i < filter_count; i++) { for (size_t i = 0; i < filter_count; i++) {
filterset.insert(filter[i]); filterset.insert(filter[i]);
} }
HANDLE hProcessSnap; HANDLE hProcessSnap;
HANDLE hProcess; HANDLE hProcess;
PROCESSENTRY32 pe32; PROCESSENTRY32 pe32;
DWORD dwPriorityClass; DWORD dwPriorityClass;
ProcessInfos pinfos = {}; ProcessInfos pinfos = {};
// Take a snapshot of all processes in the system. // Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) { if (hProcessSnap == INVALID_HANDLE_VALUE) {
outputLastErrorHelper(L"CreateToolhelp32Snapshot (of processes)", output); outputLastErrorHelper(L"CreateToolhelp32Snapshot (of processes)", output);
return(pinfos); return(pinfos);
} }
// Set the size of the structure before using it. // Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32); pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process, // Retrieve information about the first process,
// and exit if unsuccessful // and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32)) { if (!Process32First(hProcessSnap, &pe32)) {
outputLastErrorHelper((L"Process32First"), output); // show cause of failure outputLastErrorHelper((L"Process32First"), output); // show cause of failure
CloseHandle(hProcessSnap); // clean the snapshot object CloseHandle(hProcessSnap); // clean the snapshot object
return(pinfos); return(pinfos);
} }
// Now walk the snapshot of processes, and // Now walk the snapshot of processes, and
// display information about each process in turn // display information about each process in turn
do { do {
if (filterset.find(pe32.szExeFile) != filterset.end()) { continue; } if (filterset.find(pe32.szExeFile) != filterset.end()) { continue; }
ProcessInfo pinfo = {}; ProcessInfo pinfo = {};
pinfo.name = pe32.szExeFile; pinfo.name = pe32.szExeFile;
// Retrieve the priority class. // Retrieve the priority class.
dwPriorityClass = 0; dwPriorityClass = 0;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
if (hProcess == NULL) if (hProcess == NULL)
outputLastErrorHelper((L"OpenProcess"), output); outputLastErrorHelper((L"OpenProcess"), output);
else { else {
dwPriorityClass = GetPriorityClass(hProcess); dwPriorityClass = GetPriorityClass(hProcess);
if (!dwPriorityClass) if (!dwPriorityClass)
outputLastErrorHelper((L"GetPriorityClass"), output); outputLastErrorHelper((L"GetPriorityClass"), output);
} }
pinfo.pid = pe32.th32ProcessID; pinfo.pid = pe32.th32ProcessID;
pinfo.ppid = pe32.th32ParentProcessID; pinfo.ppid = pe32.th32ParentProcessID;
pinfo.thread_count = pe32.cntThreads; pinfo.thread_count = pe32.cntThreads;
pinfo.priority_base = pe32.pcPriClassBase; pinfo.priority_base = pe32.pcPriClassBase;
if (dwPriorityClass) { if (dwPriorityClass) {
pinfo.priority_class = dwPriorityClass; pinfo.priority_class = dwPriorityClass;
} }
// List the modules and threads associated with this process // List the modules and threads associated with this process
pinfo.modules = getProcessModules(pe32.th32ProcessID, output); pinfo.modules = getProcessModules(pe32.th32ProcessID, output);
// try to get process exe path // try to get process exe path
if (!pinfo.modules.empty()) { if (!pinfo.modules.empty()) {
pinfo.path = pinfo.modules.front().path; pinfo.path = pinfo.modules.front().path;
} }
if (!withModules) { if (!withModules) {
pinfo.modules.clear(); pinfo.modules.clear();
} }
if (withThreads) { if (withThreads) {
pinfo.threads = getProcessThreads(pe32.th32ProcessID, output); pinfo.threads = getProcessThreads(pe32.th32ProcessID, output);
} }
// try to get process exe path again // try to get process exe path again
if (pinfo.path.empty()) { if (pinfo.path.empty()) {
if (!hProcess) { if (!hProcess) {
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32.th32ProcessID); hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32.th32ProcessID);
} }
if (hProcess) { if (hProcess) {
wchar_t path[4096]; DWORD len = 4096; wchar_t path[4096]; DWORD len = 4096;
if (QueryFullProcessImageNameW(hProcess, 0, path, &len)) { if (QueryFullProcessImageNameW(hProcess, 0, path, &len)) {
pinfo.path = path; pinfo.path = path;
} else { } else {
outputLastErrorHelper(L"QueryFullProcessImageNameW", output); outputLastErrorHelper(L"QueryFullProcessImageNameW", output);
} }
} else { } else {
outputLastErrorHelper(L"OpenProcess", output); outputLastErrorHelper(L"OpenProcess", output);
} }
} }
pinfos.emplace_back(pinfo); pinfos.emplace_back(pinfo);
if(hProcess){ CloseHandle(hProcess); } if(hProcess){ CloseHandle(hProcess); }
} while (Process32Next(hProcessSnap, &pe32)); } while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap); CloseHandle(hProcessSnap);
return(pinfos); return(pinfos);
} }
} // namespace win32 } // namespace win32
} // namespace jlib } // namespace jlib
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
#include <vector> #include <vector>
namespace jlib namespace jlib
{ {
namespace win32 namespace win32
{ {
inline long width(::LPCRECT rc) { inline long width(::LPCRECT rc) {
return rc->right - rc->left; return rc->right - rc->left;
} }
inline long height(::LPCRECT rc) { inline long height(::LPCRECT rc) {
return rc->bottom - rc->top; return rc->bottom - rc->top;
} }
inline void deflateRect(::LPRECT rc, int l, int t, int r, int b) { inline void deflateRect(::LPRECT rc, int l, int t, int r, int b) {
rc->left += l; rc->left += l;
rc->top += t; rc->top += t;
rc->right -= r; rc->right -= r;
rc->bottom -= b; rc->bottom -= b;
} }
inline void inflateRect(::LPRECT rc, int l, int t, int r, int b) { inline void inflateRect(::LPRECT rc, int l, int t, int r, int b) {
rc->left -= l; rc->left -= l;
rc->top -= t; rc->top -= t;
rc->right += r; rc->right += r;
rc->bottom += b; rc->bottom += b;
} }
//! 将矩形平均分割成 n 份,间距 2*gap, n 必须是平方数 x^2, x={1,2,3...} //! 将矩形平均分割成 n 份,间距 2*gap, n 必须是平方数 x^2, x={1,2,3...}
inline std::vector<::RECT> splitRect(::LPCRECT rc, int n, int gap = 50) { inline std::vector<::RECT> splitRect(::LPCRECT rc, int n, int gap = 50) {
std::vector<::RECT> v; std::vector<::RECT> v;
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
v.push_back(*rc); v.push_back(*rc);
} }
double l = sqrt(n); double l = sqrt(n);
int line = int(l); int line = int(l);
int col_step = (int)(width(rc) / line); int col_step = (int)(width(rc) / line);
int row_step = (int)(height(rc) / line); int row_step = (int)(height(rc) / line);
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
v[i].left = rc->left + (i % line) * col_step; v[i].left = rc->left + (i % line) * col_step;
v[i].right = v[i].left + col_step; v[i].right = v[i].left + col_step;
v[i].top = rc->top + (i / line) * row_step; v[i].top = rc->top + (i / line) * row_step;
v[i].bottom = v[i].top + row_step; v[i].bottom = v[i].top + row_step;
deflateRect(&v[i], gap, gap, gap, gap); deflateRect(&v[i], gap, gap, gap, gap);
} }
return v; return v;
}; };
//! 将矩形水平平均分割为 n 份矩形, 当 hgap == -1 时,分割出的矩形与源矩形保持比例 //! 将矩形水平平均分割为 n 份矩形, 当 hgap == -1 时,分割出的矩形与源矩形保持比例
inline std::vector<::RECT> split_rect_horizontal(::LPCRECT rc, int n, int wgap = 50, int hgap = -1) { inline std::vector<::RECT> split_rect_horizontal(::LPCRECT rc, int n, int wgap = 50, int hgap = -1) {
std::vector<::RECT> v; std::vector<::RECT> v;
int w = (width(rc) - (n + 1) * wgap) / n; int w = (width(rc) - (n + 1) * wgap) / n;
if (hgap == -1) { if (hgap == -1) {
double ratio = (rc->right - rc->left) * 1.0 / (rc->bottom - rc->top); double ratio = (rc->right - rc->left) * 1.0 / (rc->bottom - rc->top);
int h = static_cast<int>(w / ratio); int h = static_cast<int>(w / ratio);
hgap = (height(rc) - h) / 2; hgap = (height(rc) - h) / 2;
} }
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
::RECT r = *rc; ::RECT r = *rc;
r.left += i * w + (i + 1) * wgap; r.left += i * w + (i + 1) * wgap;
r.right = r.left + w; r.right = r.left + w;
r.top = rc->top + hgap; r.top = rc->top + hgap;
r.bottom = rc->bottom - hgap; r.bottom = rc->bottom - hgap;
v.push_back(r); v.push_back(r);
} }
return v; return v;
} }
/** /**
* @brief 从矩形内获取六边形顶点 * @brief 从矩形内获取六边形顶点
* @param rc 矩形 * @param rc 矩形
* @param spliter 六边形的边长是矩形边长的 1/spliter * @param spliter 六边形的边长是矩形边长的 1/spliter
* @note rc's width / spliter = (rc's width - hexagon's side length) / 2 * @note rc's width / spliter = (rc's width - hexagon's side length) / 2
* @return 六边形的六个顶点坐标,从上边左侧点顺时针排列 * @return 六边形的六个顶点坐标,从上边左侧点顺时针排列
*/ */
inline std::vector<::POINT> getHexagonVertexesFromRect(::LPCRECT rc, float spliter = 3.0) { inline std::vector<::POINT> getHexagonVertexesFromRect(::LPCRECT rc, float spliter = 3.0) {
if (!rc) { return std::vector<::POINT>(); } if (!rc) { return std::vector<::POINT>(); }
if (spliter == 0.0) { spliter = 3.0; } if (spliter == 0.0) { spliter = 3.0; }
std::vector<::POINT> v; std::vector<::POINT> v;
auto w = rc->right - rc->left; auto w = rc->right - rc->left;
auto h = rc->bottom - rc->top; auto h = rc->bottom - rc->top;
auto ww = static_cast<int>(w / spliter); auto ww = static_cast<int>(w / spliter);
auto hh = static_cast<int>(h / spliter); auto hh = static_cast<int>(h / spliter);
::POINT pt; ::POINT pt;
pt.x = rc->left; pt.x = rc->left;
pt.y = rc->top + hh; pt.y = rc->top + hh;
v.push_back(pt); v.push_back(pt);
pt.x = rc->left + ww; pt.x = rc->left + ww;
pt.y = rc->top; pt.y = rc->top;
v.push_back(pt); v.push_back(pt);
pt.x = rc->right - ww; pt.x = rc->right - ww;
v.push_back(pt); v.push_back(pt);
pt.x = rc->right; pt.x = rc->right;
pt.y = rc->top + hh; pt.y = rc->top + hh;
v.push_back(pt); v.push_back(pt);
pt.y = rc->bottom - hh; pt.y = rc->bottom - hh;
v.push_back(pt); v.push_back(pt);
pt.x = rc->right - ww; pt.x = rc->right - ww;
pt.y = rc->bottom; pt.y = rc->bottom;
v.push_back(pt); v.push_back(pt);
pt.x = rc->left + ww; pt.x = rc->left + ww;
v.push_back(pt); v.push_back(pt);
pt.x = rc->left; pt.x = rc->left;
pt.y = rc->bottom - hh; pt.y = rc->bottom - hh;
v.push_back(pt); v.push_back(pt);
return v; return v;
} }
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment