Commit c9174ca4 authored by captainwong's avatar captainwong

update

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