CMake+QT+大漠插件的桌面应用开发
说明
在CMake+大漠插件的应用开发——处理dm.dll,免注册调用大漠插件中已经说明了如何免注册调用大漠插件,以及做了几个简单的功能调用(查找窗口、截图) 下面来利用QT做一个简单的窗口查找、截图的桌面工具应用,功能点如下
点击“注册”选项完成大漠插件的注册。 用户在文本框输入窗口标题后,点击“查询”按钮,可对包含该标题的窗口进行查询。 提供表格展示查询到的窗口信息。 点击“截图”按钮,对选中的窗口进行截图并保存。 界面如下
目前主窗口的UI操作和大漠的调用是在一个线程里面的,当大漠调用时间过长时会出现UI界面卡顿的现象,下一篇将会给出如何处理这种问题的示例。
环境
版本/规范 备注 平台 win32 操作系统为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工具
项目结构
新建一个项目 qt_dm_demo_x_01 将下载好的 dm.dll 文件以及处理好的 dm.tlh、dm.tli 文件放置到项目的 external 目录下
注:dm.tlh、dm.tli 文件的生成请参考 CMake+大漠插件的应用开发——处理dm.dll,免注册调用大漠插件
qt_dm_demo_x_01 # 项目目录
--|cmake-build-debug-visual-studio # 工程构建目录,存临时生成的文件
--|--|...
--|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 # 字符串工具
配置编译环境
配置工具链
和CMake+大漠插件的应用开发——处理dm.dll,免注册调用大漠插件中保持一致即可 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)
set(CMAKE_PREFIX_PATH "C:/Qt/Qt5.12.12/5.12.12/msvc2017")
find_package(Qt5 COMPONENTS
Core
Gui
Widgets
REQUIRED)
add_executable(${PROJECT_NAME} main.cpp
strutils.cpp strutils.h
dmutil.cpp dmutil.h
mymainwindow.cpp mymainwindow.h mymainwindow.ui
)
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}")
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})
代码
# ifndef DM_DEMO_X_DMUTIL_H
# define DM_DEMO_X_DMUTIL_H
# include <string>
# include <vector>
# include "./external/dm.tlh"
# define DM_LIB_PATH L "./external/dm.dll"
using namespace std;
struct MyWindow {
long hwnd;
wstring title;
long processId;
} ;
Idmsoft * GetDmObject ( ) ;
Idmsoft * initialDMAndRegVIP ( ) ;
void getMatchedWindows ( vector< MyWindow> & baseVec, Idmsoft * pDm, const wstring& title, const wstring& processName = L"" ) ;
# endif
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 ;
}
cout << "===> DM version: " << ( char * ) pDm-> Ver ( ) << endl;
long regResult = pDm-> Reg ( L"注册码" , L"版本附加信息(附加码)" ) ;
if ( regResult != 1 ) {
cout << "===> Account registration failed ! code = " << regResult << endl;
return nullptr ;
}
cout << "===> Account registration successful ! " << 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, "," ) ;
baseVec. reserve ( hwndStrVec. size ( ) ) ;
for ( const string_view& element : hwndStrVec) {
long curHwnd = viewToInt ( element) ;
const _bstr_t & curTitle = pDm-> GetWindowTitle ( curHwnd) ;
long processId = pDm-> GetWindowProcessId ( curHwnd) ;
baseVec. push_back ( { curHwnd, { curTitle} , processId} ) ;
}
}
# ifndef DM_DEMO_X_STRUTILS_H
# define DM_DEMO_X_STRUTILS_H
# include <string>
# include <string_view>
# include <iostream>
# include <vector>
using namespace std;
vector< string_view> splitSV ( string_view content, string_view delim = " " ) ;
int viewToInt ( string_view content) ;
# endif
# 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;
}
<?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> 窗口标题:</ 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>
# 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 = "告警" ) ;
void doRegDM ( Idmsoft * * pDm) ;
void doFindWindow ( Idmsoft * pDm, const QString & title) ;
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
# 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 ) ;
setFixedSize ( 1280 , 720 ) ;
ui-> tableWidget-> setColumnCount ( 3 ) ;
ui-> tableWidget-> setHorizontalHeaderLabels ( QStringList ( ) << "进程ID" << "句柄" << "标题" ) ;
ui-> tableWidget-> horizontalHeader ( ) -> setStretchLastSection ( true ) ;
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) ;
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 ;
}
getMatchedWindows ( windowVec, pDm, title. toStdWString ( ) ) ;
if ( windowVec. empty ( ) ) {
cout << "can not find such window" << endl;
this -> showTableView ( false , "没有找到包含该标题的窗口!" , 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 ) {
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 ( ) ;
}
# 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 ( ) ;
}