CMake+QT+大漠插件的桌面應(yīng)用開發(fā)
簡介
- 在CMake+大漠插件的應(yīng)用開發(fā)——處理dm.dll,免注冊調(diào)用大漠插件中已經(jīng)說明了如何免注冊調(diào)用大漠插件,以及做了幾個簡單的功能調(diào)用(查找窗口、截圖)
- 下面來利用QT和大漠插件做一個簡單的窗口查找、截圖的桌面工具應(yīng)用,功能點如下
- 點擊“注冊”選項完成大漠插件的注冊。
- 用戶在文本框輸入窗口標(biāo)題后,點擊“查詢”按鈕,可對包含該標(biāo)題的窗口進(jìn)行查詢。
- 提供表格展示查詢到的窗口信息。
- 點擊“截圖”按鈕,對選中的窗口進(jìn)行截圖并保存。
- 界面如下
文章來源地址http://www.zghlxwxcb.cn/news/detail-803140.html
- 目前主窗口的UI操作和大漠的調(diào)用是在一個線程里面的,當(dāng)大漠調(diào)用時間過長時會出現(xiàn)UI界面卡頓的現(xiàn)象,下一篇CMake+QT+大漠插件的桌面應(yīng)用開發(fā)(QThread)將會給出如何處理這種問題的示例。
環(huán)境
版本/規(guī)范 | 備注 | |
---|---|---|
平臺 | win32 | 操作系統(tǒng)為Windows10 |
CMake | 3.27.8 | CLion自帶 |
C++ | 17 | |
Toolchain | VisualStudio 2022 | 只用其工具鏈,記得先安裝好 |
QT | 5.12.12 | 安裝時選擇msvc2017,不要64位的 |
DM | 7.2353 | |
CLion | 2023.3.2 | 你也可以用其他IDE工具 |
- 啟動IDE時,記得以管理員模式啟動
項目結(jié)構(gòu)
- 新建一個項目 qt_dm_demo_x_01
- 將下載好的 dm.dll 文件以及處理好的 dm.tlh、dm.tli 文件放置到項目的 external 目錄下
- 注:dm.tlh、dm.tli 文件的生成請參考 CMake+大漠插件的應(yīng)用開發(fā)——處理dm.dll,免注冊調(diào)用大漠插件
qt_dm_demo_x_01 # 項目目錄
--|cmake-build-debug-visual-studio # 工程構(gòu)建目錄,存臨時生成的文件
--|--|...
--|external # 引入第三方庫文件的所在的文件夾
--|--|dm.dll # 大漠插件的dll
--|--|dm.tlh
--|--|dm.tli
--CMakeLists.txt # CMake腳本文件
--dmutil.cpp # 大漠的功能封裝工具
--dmutil.h # 大漠的功能封裝工具
--main.cpp # 程序入口
--mymainwindow.cpp # 主窗口
--mymainwindow.h # 主窗口
--mymainwindow.ui # 主窗口的UI文件
--strutils.cpp # 字符串工具
--strutils.h # 字符串工具
配置編譯環(huán)境
- 配置工具鏈
- 和CMake+大漠插件的應(yīng)用開發(fā)——處理dm.dll,免注冊調(diào)用大漠插件中保持一致即可
- CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.27)
project(qt_dm_demo_x_01)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# QT安裝的msvc地址
set(CMAKE_PREFIX_PATH "C:/Qt/Qt5.12.12/5.12.12/msvc2017")
# 查找QT組件包
find_package(Qt5 COMPONENTS
Core
Gui
Widgets
REQUIRED)
# 生成可執(zhí)行文件
add_executable(${PROJECT_NAME} main.cpp
strutils.cpp strutils.h
dmutil.cpp dmutil.h
mymainwindow.cpp mymainwindow.h mymainwindow.ui
)
# 鏈接需要的QT庫
target_link_libraries(${PROJECT_NAME}
Qt5::Core
Qt5::Gui
Qt5::Widgets
)
target_compile_definitions(${PROJECT_NAME} PRIVATE
-DWIN32
# -D_DEBUG
-D_WINDOWS
-D_UNICODE
-DUNICODE
)
message(STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")
# 拷貝庫文件到生成的可執(zhí)行文件旁邊
if (WIN32 AND NOT DEFINED CMAKETOOLCHAIN_FILE)
set(DEBUG_SUFFIX)
if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug")
set(DEBUG_SUFFIX "d")
endif ()
set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}")
if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
endif ()
endif ()
if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
"$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll"
"$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
endif ()
foreach (QT_LIB Core Gui Widgets)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${QT_INSTALL_PATH}/bin/Qt5${QT_LIB}${DEBUG_SUFFIX}.dll"
"$<TARGET_FILE_DIR:${PROJECT_NAME}>")
endforeach (QT_LIB)
endif ()
# 拷貝資源文件 dm.dll
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/external DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
代碼
- dmutil.h
#ifndef DM_DEMO_X_DMUTIL_H
#define DM_DEMO_X_DMUTIL_H
#include <string>
#include <vector>
// #import "./external/dm.dll" no_namespace
#include "./external/dm.tlh"
#define DM_LIB_PATH L"./external/dm.dll"
using namespace std;
struct MyWindow {
long hwnd;
wstring title;
long processId;
};
/**
* 注冊dm.dll,獲取大漠實例
* @return 大漠實例
*/
Idmsoft *GetDmObject();
/**
* 初始化大漠插件,并注冊用戶VIP
* @return 大漠實例
*/
Idmsoft *initialDMAndRegVIP();
/**
* 獲取匹配的窗口
* @param baseVec 保存window的容器
* @param pDm 大漠插件
* @param title 窗口標(biāo)題中的字段(模糊匹配)
* @param processName 進(jìn)程名稱(精確匹配,不區(qū)分大小寫),默認(rèn)不會根據(jù)進(jìn)程名篩選窗口
*/
void getMatchedWindows(vector<MyWindow>& baseVec, Idmsoft *pDm, const wstring& title, const wstring& processName = L"");
#endif //DM_DEMO_X_DMUTIL_H
- dmutil.cpp(記得填入自己的 注冊碼 和 附加碼)
#include <iostream>
#include <sstream>
#include <string_view>
#include <vector>
#include "dmutil.h"
#include "strutils.h"
using namespace std;
Idmsoft *GetDmObject() {
Idmsoft *m_dm = nullptr;
bool m_bInit = false;
typedef HRESULT(_stdcall
*pfnGCO)(REFCLSID, REFIID, void**);
pfnGCO fnGCO = nullptr;
HINSTANCE hdllInst = LoadLibrary(DM_LIB_PATH);
if (hdllInst == nullptr) {
cout << "Load library 'dm.dll' failed ! DM_LIB_PATH = " << DM_LIB_PATH << endl;
return nullptr;
}
fnGCO = (pfnGCO) GetProcAddress(hdllInst, "DllGetClassObject");
if (fnGCO != nullptr) {
IClassFactory *pcf = nullptr;
HRESULT hr = (fnGCO)(__uuidof(dmsoft), IID_IClassFactory, (void **) &pcf);
if (SUCCEEDED(hr) && (pcf != nullptr)) {
hr = pcf->CreateInstance(nullptr, __uuidof(Idmsoft), (void **) &m_dm);
if ((SUCCEEDED(hr) && (m_dm != nullptr)) == FALSE) {
cout << "Create instance 'Idmsoft' failed !" << endl;
return nullptr;
}
}
pcf->Release();
m_bInit = true;
}
return m_dm;
}
Idmsoft *initialDMAndRegVIP() {
Idmsoft *pDm = GetDmObject();
if (pDm == nullptr) {
cout << "===> dm.dll registration failed !" << endl;
return nullptr;
}
// 注冊dm.dll成功,打印版本
cout << "===> DM version: " << (char *) pDm->Ver() << endl;
// 注冊用戶(同一程序下,只需注冊一次,后續(xù)不用重復(fù)注冊)
long regResult = pDm->Reg(L"注冊碼", L"版本附加信息(附加碼)");
if (regResult != 1) {
cout << "===> Account registration failed ! code = " << regResult << endl;
return nullptr;
}
cout << "===> Account registration successful ! " << endl;
// long releaseRes = pDm->ReleaseRef();
// cout << "===> ReleaseCode = " << releaseRes << endl;
return pDm;
}
void getMatchedWindows(vector<MyWindow>& baseVec, Idmsoft *pDm, const wstring& title, const wstring& processName) {
// 獲取匹配的窗口句柄
_bstr_t hwnds;
if (!processName.empty()) {
hwnds = pDm->EnumWindowByProcess(processName.c_str(), title.c_str(), L"", 1 + 8 + 16);
} else {
hwnds = pDm->EnumWindow(0, title.c_str(), L"", 1 + 4 + 8 + 16);
}
// 拆分找到的多個窗口句柄
string content(hwnds);
vector<string_view> hwndStrVec = splitSV(content, ",");
// 裝入容器vector
baseVec.reserve(hwndStrVec.size());
for (const string_view& element : hwndStrVec) {
long curHwnd = viewToInt(element);
// 獲取窗口title
const _bstr_t &curTitle = pDm->GetWindowTitle(curHwnd);
// const _bstr_t &className = pDm->GetWindowClass(curHwnd);
long processId = pDm->GetWindowProcessId(curHwnd);
baseVec.push_back({curHwnd, {curTitle}, processId});
}
}
- strutils.h
#ifndef DM_DEMO_X_STRUTILS_H
#define DM_DEMO_X_STRUTILS_H
#include <string>
#include <string_view>
#include <iostream>
#include <vector>
using namespace std;
/**
* 按分隔符拆分字符串內(nèi)容為vector,string_view更加高效
* @param content 待分隔的字符串
* @param delim 分隔符,默認(rèn)為空格
* @return vector容器
*/
vector<string_view> splitSV(string_view content, string_view delim = " ");
/**
* string_view 轉(zhuǎn) int,失敗時拋出異常
* @param content 字符串內(nèi)容view
* @return int值
*/
int viewToInt(string_view content);
#endif //DM_DEMO_X_STRUTILS_H
- strutils.cpp
#include <sstream>
#include <string>
#include <vector>
#include <charconv>
#include "strutils.h"
vector<string_view> splitSV(string_view content, string_view delim) {
vector<string_view> output;
size_t first = 0;
while (first < content.size()) {
const auto second = content.find_first_of(delim, first);
if (first != second)
output.emplace_back(content.substr(first, second - first));
if (second == string_view::npos)
break;
first = second + 1;
}
return output;
}
int viewToInt(string_view content) {
int num;
auto result = std::from_chars(content.data(), content.data() + content.size(), num);
if (result.ec == std::errc::invalid_argument) {
throw std::runtime_error("Could not convert.");
}
return num;
}
- mymainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MyMainWindow</class>
<widget class="QMainWindow" name="MyMainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>窗口查詢程序</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>窗口標(biāo)題:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="edtTitle">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnQuery">
<property name="text">
<string>模糊查詢</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCapture">
<property name="text">
<string>截圖(選中行)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableWidget"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuOperation">
<property name="title">
<string>菜單</string>
</property>
<addaction name="actionReg"/>
</widget>
<addaction name="menuOperation"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionReg">
<property name="text">
<string>注冊DM</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
- mymainwindow.h
#ifndef QT_DM_DEMO_X_MYMAINWINDOW_H
#define QT_DM_DEMO_X_MYMAINWINDOW_H
#include <QMainWindow>
#include "dmutil.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MyMainWindow; }
QT_END_NAMESPACE
class MyMainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MyMainWindow(QWidget *parent = nullptr);
~MyMainWindow() override;
public:
void showInfo(const QString &message, const QString &title = "提示");
void showWarn(const QString &message, const QString &title = "告警");
/**
* 注冊大漠
* @param pDm 大漠插件,待賦值
*/
void doRegDM(Idmsoft **pDm);
/**
* 查詢匹配的窗口
* @param pDm 大漠插件
* @param title 窗口標(biāo)題(模糊查詢)
*/
void doFindWindow(Idmsoft *pDm, const QString &title);
/**
* 對窗口截圖
* @param pDm 大漠插件
* @param hwnd 窗口句柄
*/
void doCaptureWindow(Idmsoft *pDm, long hwnd);
public slots:
void showMessageBox(bool result, const QString &message);
void showTableView(bool result, const QString &msg, const vector<MyWindow> &windowVec);
private:
Ui::MyMainWindow *ui;
Idmsoft *pCommonDm = nullptr;
};
#endif //QT_DM_DEMO_X_MYMAINWINDOW_H
- mymainwindow.cpp
// You may need to build the project (run Qt uic code generator) to get "ui_MyMainWindow.h" resolved
#include <QFont>
#include <QHeaderView>
#include <QMessageBox>
#include <QPushButton>
#include <QAction>
#include <QString>
#include <QTableWidgetItem>
#include <QObject>
#include <QVector>
#include <iostream>
#include "mymainwindow.h"
#include "ui_MyMainWindow.h"
using namespace std;
MyMainWindow::MyMainWindow(QWidget *parent) :
QMainWindow(parent), ui(new Ui::MyMainWindow) {
ui->setupUi(this);
// Init Views
setFixedSize(1280, 720);
ui->tableWidget->setColumnCount(3);
ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "進(jìn)程ID" << "句柄" << "標(biāo)題");
ui->tableWidget->horizontalHeader()->setStretchLastSection(true); // 最后一列自動鋪滿表格
// ui->tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
ui->tableWidget->horizontalHeader()->setHighlightSections(false);
ui->tableWidget->horizontalHeader()->setStyleSheet("QHeaderView::section{background:gray;}");
ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
QFont font = ui->tableWidget->horizontalHeader()->font();
font.setBold(true);
ui->tableWidget->horizontalHeader()->setFont(font);
ui->tableWidget->setStyleSheet("QTableWidget::item:hover { background-color: lightblue; }");
ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); // 禁止編輯
ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); // 選中整行
// Init Listener
// 注冊大漠
connect(ui->actionReg, &QAction::triggered, [this]() {
ui->actionReg->setEnabled(false);
this->doRegDM(&this->pCommonDm);
ui->actionReg->setEnabled(true);
});
// 查找窗口
connect(ui->btnQuery, &QPushButton::clicked, [this]() {
ui->btnQuery->setEnabled(false);
this->doFindWindow(this->pCommonDm, ui->edtTitle->text());
ui->btnQuery->setEnabled(true);
});
// 截圖
connect(ui->btnCapture, &QPushButton::clicked, [this]() {
ui->btnCapture->setEnabled(false);
// 獲取選中行的句柄列的字段
const QList<QTableWidgetItem *> &selectedItems = ui->tableWidget->selectedItems();
if (selectedItems.size() >= 2) {
QTableWidgetItem *item = selectedItems.at(1);
const QString &hwnd = item->data(Qt::DisplayRole).toString();
bool res = false;
long hwndL = hwnd.toLong(&res, 0);
cout << res << endl;
if (res) {
this->doCaptureWindow(this->pCommonDm, hwndL);
} else {
this->showWarn("選中行的窗口句柄解析異常!");
}
} else {
this->showWarn("請選中列表中的其中一行!");
}
ui->btnCapture->setEnabled(true);
});
}
MyMainWindow::~MyMainWindow() {
delete ui;
}
void MyMainWindow::showInfo(const QString &message, const QString &title) {
QMessageBox::information(this, title, message);
}
void MyMainWindow::showWarn(const QString &message, const QString &title) {
QMessageBox::critical(this, title, message);
}
void MyMainWindow::showMessageBox(const bool result, const QString& message) {
if (result) {
this->showInfo(message);
} else {
this->showWarn(message);
}
}
void MyMainWindow::showTableView(bool result, const QString &msg, const vector<MyWindow> &windowVec) {
if (result) {
auto rowNum = windowVec.size();
ui->tableWidget->setRowCount(rowNum);
for (int i = 0; i < rowNum; ++i) {
const MyWindow &item = windowVec[i];
ui->tableWidget->setItem(i, 0, new QTableWidgetItem(QString::number(item.processId)));
ui->tableWidget->setItem(i, 1, new QTableWidgetItem(QString::number(item.hwnd)));
ui->tableWidget->setItem(i, 2, new QTableWidgetItem(QString::fromStdWString(item.title)));
}
} else {
this->showWarn(msg);
}
}
void MyMainWindow::doRegDM(Idmsoft **pDm) {
cout << "========== Initial DM ............ ==========" << endl;
*pDm = initialDMAndRegVIP();
if (*pDm == nullptr) {
cout << "========== Initial DM <Failed> ==========" << endl;
showMessageBox(false, "DM 注冊失??!");
return;
}
cout << "========== Initial DM <Successful> ==========" << endl;
cout << endl;
showMessageBox(true, "DM 注冊完成!");
}
void MyMainWindow::doFindWindow(Idmsoft *pDm, const QString &title) {
vector<MyWindow> windowVec;
if (pDm == nullptr) {
cout << "this->pCommonDm == nullptr" << endl;
this->showTableView(false, "請先在菜單中完成注冊!", windowVec);
return;
}
// 找一下包含title的窗口
getMatchedWindows(windowVec, pDm, title.toStdWString());
if (windowVec.empty()) {
cout << "can not find such window" << endl;
this->showTableView(false, "沒有找到包含該標(biāo)題的窗口!", windowVec);
return;
}
this->showTableView(true, "成功!", windowVec);
}
void MyMainWindow::doCaptureWindow(Idmsoft *pDm, long hwnd) {
if (pDm == nullptr) {
cout << "this->pCommonDm == nullptr" << endl;
this->showMessageBox(false, "請先在菜單中完成注冊!");
return;
}
// 綁定窗口句柄
long dmBind = pDm->BindWindowEx(
hwnd,
"normal",
"normal",
"normal",
"",
0
);
if (dmBind == 1) {
// 恢復(fù)并激活指定窗口,置頂窗口,
pDm->SetWindowState(hwnd, 12);
pDm->SetWindowState(hwnd, 8);
pDm->delay(600);
// 延遲一下截圖,存到相對路徑
wstring filename = wstring(L"./capture_window_").append(std::to_wstring(hwnd)).append(L".bmp");
long retCap = pDm->Capture(0, 0, 2000, 2000, filename.c_str());
if (retCap != 1) {
cout << "capture failed" << endl;
this->showMessageBox(false, "截圖失敗!");
} else {
cout << "capture success" << endl;
this->showMessageBox(true, QString::fromStdWString(L"截圖成功,保存地址為: " + filename));
}
// 取消置頂窗口
pDm->SetWindowState(hwnd, 9);
} else {
cout << "DM BindWindow failed" << endl;
this->showMessageBox(false, "綁定窗口異常!");
}
pDm->UnBindWindow();
}
- main.cpp
#include <QApplication>
#include <iostream>
#include "mymainwindow.h"
using namespace std;
int main(int argc, char *argv[]) {
setlocale(LC_ALL, "chs");
QApplication a(argc, argv);
MyMainWindow mainWindow;
mainWindow.show();
return QApplication::exec();
}
文章來源:http://www.zghlxwxcb.cn/news/detail-803140.html
到了這里,關(guān)于CMake+QT+大漠插件的桌面應(yīng)用開發(fā)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!