From 615d372eeb197be7cb22e72c3a398a69a4bb7055 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 5 Jun 2021 10:08:50 -0600 Subject: [PATCH 1/9] missing vcredist causes some weird stuff... --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 02ae573..0f94195 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ Watch the [tutorial video](https://tinyurl.com/textractor-tutorial) for a quick ## Download Official stable releases of Textractor can be found [here](https://github.com/Artikash/Textractor/releases).
-Experimental builds of Textractor from the latest source can be found [here](https://ci.appveyor.com/project/Artikash/textractor/history) (in the 'Artifacts' section of a job).
The last release of ITHVNR can be found [here](https://drive.google.com/open?id=13aHF4uIXWn-3YML_k2YCDWhtGgn5-tnO).
-Try running vcredist if you get an error when starting Textractor. +Experimental builds of Textractor (with debug info) from the latest source can be found [here](https://ci.appveyor.com/project/Artikash/textractor/history) in the 'Artifacts' section of each job.
+Try running vcredist if you get an error when starting Textractor or if nothing happens when you try attaching to a game. ## Features From dd4b8cfbb535cc10df7e5d29e81adc7ce1ff8b52 Mon Sep 17 00:00:00 2001 From: tera8m4 <82479019+tera8m4@users.noreply.github.com> Date: Sun, 2 May 2021 07:05:54 -0700 Subject: [PATCH 2/9] Create small helpers to get icon from executable --- GUI/CMakeLists.txt | 9 +++++++-- GUI/utils/windowshelpers.cpp | 30 ++++++++++++++++++++++++++++++ GUI/utils/windowshelpers.h | 9 +++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 GUI/utils/windowshelpers.cpp create mode 100644 GUI/utils/windowshelpers.h diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index be99474..0b07df8 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -1,6 +1,10 @@ include(QtUtils) msvc_registry_search() -find_qt5(Core Widgets) +find_qt5(Core Widgets WinExtras) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) add_executable(Textractor WIN32 main.cpp @@ -10,11 +14,12 @@ add_executable(Textractor WIN32 host/host.cpp host/textthread.cpp host/hookcode.cpp + utils/windowshelpers.cpp Textractor.rc Textractor.ico ) target_precompile_headers(Textractor REUSE_FROM pch) -target_link_libraries(Textractor Qt5::Widgets shell32 winhttp) +target_link_libraries(Textractor Qt5::Widgets Qt5::WinExtras shell32 winhttp) if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5Core.dll AND NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5Cored.dll) add_custom_command(TARGET Textractor diff --git a/GUI/utils/windowshelpers.cpp b/GUI/utils/windowshelpers.cpp new file mode 100644 index 0000000..2d657d9 --- /dev/null +++ b/GUI/utils/windowshelpers.cpp @@ -0,0 +1,30 @@ +#include "windowshelpers.h" + +#include +#include +#include +#include +#include + +#include + +namespace WindowsHepers { + HICON GetIconHandlerFromExe(const wchar_t* const filePath) + { + HICON bigIcon; + HICON smallIcon; + ExtractIconEx(filePath, 0, &bigIcon, &smallIcon, 1); + return bigIcon != 0 ? bigIcon : smallIcon; + } + + QIcon CreateQIconFromHIcon(const HICON hIcon) + { + if (hIcon) + { + const QPixmap& pixmap = QtWin::fromHICON(hIcon); + return QIcon(pixmap); + } + const QStyle* style = QApplication::style(); + return style->standardIcon(QStyle::SP_ComputerIcon); + } +} diff --git a/GUI/utils/windowshelpers.h b/GUI/utils/windowshelpers.h new file mode 100644 index 0000000..405d4fe --- /dev/null +++ b/GUI/utils/windowshelpers.h @@ -0,0 +1,9 @@ +#pragma once + +#include +class QIcon; +namespace WindowsHepers +{ + HICON GetIconHandlerFromExe(const wchar_t* const filePath); + QIcon CreateQIconFromHIcon(const HICON hIcon); +} From a1d3abb080d6cef5503155728db90eb697a53c27 Mon Sep 17 00:00:00 2001 From: tera8m4 <82479019+tera8m4@users.noreply.github.com> Date: Sun, 2 May 2021 07:14:18 -0700 Subject: [PATCH 3/9] Create a new attach dialog window New dialog window that shows processes with icons in QListView instead of ComboBox. --- GUI/CMakeLists.txt | 1 + GUI/attachtoprocessdialog.cpp | 92 +++++++++++++++++++++++++++++++++++ GUI/attachtoprocessdialog.h | 51 +++++++++++++++++++ GUI/attachtoprocessdialog.ui | 52 ++++++++++++++++++++ GUI/mainwindow.cpp | 58 ++++++++++++++++++---- 5 files changed, 245 insertions(+), 9 deletions(-) create mode 100644 GUI/attachtoprocessdialog.cpp create mode 100644 GUI/attachtoprocessdialog.h create mode 100644 GUI/attachtoprocessdialog.ui diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index 0b07df8..c8e17de 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(Textractor WIN32 main.cpp mainwindow.cpp extenwindow.cpp + attachtoprocessdialog.cpp host/exception.cpp host/host.cpp host/textthread.cpp diff --git a/GUI/attachtoprocessdialog.cpp b/GUI/attachtoprocessdialog.cpp new file mode 100644 index 0000000..c85b9d4 --- /dev/null +++ b/GUI/attachtoprocessdialog.cpp @@ -0,0 +1,92 @@ +#include "attachtoprocessdialog.h" +#include "ui_attachtoprocessdialog.h" +#include "utils/windowshelpers.h" + +#include +#include +#include + +namespace +{ + QString GetNameProcessFromIndex(const QModelIndex &index, const QVector>& data) + { + const int row = index.row(); + return data[row].first; + } +} + +AttachToProcessDialog::AttachToProcessDialog(QWidget *parent) : + QDialog(parent, Qt::WindowCloseButtonHint), + ui(new Ui::AttachToProcessDialog), + model(new QStandardItemModel(this)) +{ + ui->setupUi(this); + + const QIntValidator* validator = new QIntValidator(0, INT_MAX, this); + ui->lineEdit->setValidator(validator); +} + +AttachToProcessDialog::~AttachToProcessDialog() +{ + delete ui; +} + +void AttachToProcessDialog::setLabelText(const QString& text) +{ + ui->label->setText(text); +} + +void AttachToProcessDialog::setData(QVector>&& newData) +{ + data = std::move(newData); + selectedProcess.clear(); + std::sort(data.begin(), data.end(), [](const auto& left, const auto& right) { + return left.first < right.first; + }); + model->clear(); + for (const auto& [process, hIcon] : data) + { + QIcon icon = WindowsHepers::CreateQIconFromHIcon(hIcon); + auto* item = new QStandardItem(icon, process); + item->setEditable(false); + model->appendRow(item); + } + ui->listView->setModel(model); +} + +QString AttachToProcessDialog::getSelectedData() +{ + return selectedProcess.isEmpty() ? ui->lineEdit->text() : selectedProcess; +} + +void AttachToProcessDialog::on_buttonBox_accepted() +{ + accept(); +} + +void AttachToProcessDialog::on_buttonBox_rejected() +{ + reject(); +} + +void AttachToProcessDialog::on_listView_doubleClicked(const QModelIndex& index) +{ + selectedProcess = GetNameProcessFromIndex(index, data); + accept(); +} + +void AttachToProcessDialog::on_lineEdit_returnPressed() +{ + selectedProcess = ui->lineEdit->text(); + accept(); +} + +void AttachToProcessDialog::on_listView_clicked(const QModelIndex &index) +{ + selectedProcess = GetNameProcessFromIndex(index, data); +} + +void AttachToProcessDialog::on_lineEdit_editingFinished() +{ + selectedProcess = ui->lineEdit->text(); +} diff --git a/GUI/attachtoprocessdialog.h b/GUI/attachtoprocessdialog.h new file mode 100644 index 0000000..3dde6e5 --- /dev/null +++ b/GUI/attachtoprocessdialog.h @@ -0,0 +1,51 @@ +#ifndef ATTACHTOPROCESSDIALOG_H +#define ATTACHTOPROCESSDIALOG_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class AttachToProcessDialog; +} +QT_END_NAMESPACE + +class QStandardItemModel; +class QModelIndex; + +class AttachToProcessDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AttachToProcessDialog(QWidget *parent = nullptr); + void setData(QVector>&& newData); + void setLabelText(const QString& text); + QString getSelectedData(); + ~AttachToProcessDialog(); + +private slots: + void on_buttonBox_accepted(); + + void on_buttonBox_rejected(); + + void on_listView_doubleClicked(const QModelIndex &index); + + void on_lineEdit_returnPressed(); + + void on_listView_clicked(const QModelIndex &index); + + void on_lineEdit_editingFinished(); + +private: + Ui::AttachToProcessDialog* ui; + QStandardItemModel* model; + QString selectedProcess; + QVector> data; +}; + +#endif // ATTACHTOPROCESSDIALOG_H \ No newline at end of file diff --git a/GUI/attachtoprocessdialog.ui b/GUI/attachtoprocessdialog.ui new file mode 100644 index 0000000..9f3c231 --- /dev/null +++ b/GUI/attachtoprocessdialog.ui @@ -0,0 +1,52 @@ + + + AttachToProcessDialog + + + Qt::WindowModal + + + + 0 + 0 + 813 + 426 + + + + Form + + + + + + + + TextLabel + + + + + + + PID + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index e848db8..9880919 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -5,6 +5,9 @@ #include "extenwindow.h" #include "host/host.h" #include "host/hookcode.h" +#include "attachtoprocessdialog.h" +#include "utils/windowshelpers.h" + #include #include #include @@ -14,6 +17,7 @@ #include #include #include +#include extern const char* ATTACH; extern const char* LAUNCH; @@ -79,6 +83,7 @@ namespace Ui::MainWindow ui; std::atomic selectedProcessId = 0; ExtenWindow* extenWindow = nullptr; + AttachToProcessDialog* attachDialog = nullptr; std::unordered_set alreadyAttached; bool autoAttach = false, autoAttachSavedOnly = true; bool showSystemProcesses = false; @@ -152,17 +157,51 @@ namespace } void AttachProcess() - { - QMultiHash allProcesses; - for (auto [processId, processName] : GetAllProcesses()) + { + auto processes = GetAllProcesses(); + QMultiHash processesMap; + QVector> dialogData; + dialogData.reserve(processes.size()); + for (auto [processId, processName] : processes) + { if (processName && (showSystemProcesses || processName->find(L":\\Windows\\") == std::string::npos)) - allProcesses.insert(QFileInfo(S(processName.value())).fileName(), processId); + { + const auto& value = processName.value(); + const QFileInfo& fileInfo = QFileInfo(S(value)); + const QString& fileName = fileInfo.fileName(); + if (!processesMap.contains(fileName)) + { + const auto icon = WindowsHepers::GetIconHandlerFromExe(value.c_str()); + dialogData.push_back({fileName, icon}); + } + processesMap.insert(fileName, processId); + } + } + dialogData.shrink_to_fit(); + processes.clear(); - QStringList processList(allProcesses.uniqueKeys()); - processList.sort(Qt::CaseInsensitive); - if (QString process = QInputDialog::getItem(This, SELECT_PROCESS, ATTACH_INFO, processList, 0, true, &ok, Qt::WindowCloseButtonHint); ok) - if (process.toInt(nullptr, 0)) Host::InjectProcess(process.toInt(nullptr, 0)); - else for (auto processId : allProcesses.values(process)) Host::InjectProcess(processId); + attachDialog->setWindowTitle(SELECT_PROCESS); + attachDialog->setLabelText(ATTACH_INFO); + + attachDialog->setData(std::move(dialogData)); + const bool hasChosenData = attachDialog->exec() != 0; + + if (hasChosenData) + { + const QString& process = attachDialog->getSelectedData(); + const int pid = process.toInt(nullptr, 0); + if (pid) + { + Host::InjectProcess(pid); + } + else + { + for (const auto& processId : processesMap.values(process)) + { + Host::InjectProcess(processId); + } + } + } } void LaunchProcess() @@ -597,6 +636,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) This = this; ui.setupUi(this); extenWindow = new ExtenWindow(this); + attachDialog = new AttachToProcessDialog(this); for (auto [text, slot] : Array{ { ATTACH, AttachProcess }, { LAUNCH, LaunchProcess }, From eb1421c143be52f4b2090d7829d3c37f850e2c61 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 5 Jun 2021 16:29:51 -0600 Subject: [PATCH 4/9] fix style issues and use blank icons --- GUI/CMakeLists.txt | 7 +-- GUI/attachprocessdialog.cpp | 48 ++++++++++++++++++ GUI/attachprocessdialog.h | 17 +++++++ GUI/attachprocessdialog.ui | 41 ++++++++++++++++ GUI/attachtoprocessdialog.cpp | 92 ----------------------------------- GUI/attachtoprocessdialog.h | 51 ------------------- GUI/attachtoprocessdialog.ui | 52 -------------------- GUI/extenwindow.h | 5 -- GUI/mainwindow.cpp | 52 ++++++-------------- GUI/utils/windowshelpers.cpp | 30 ------------ GUI/utils/windowshelpers.h | 9 ---- 11 files changed, 121 insertions(+), 283 deletions(-) create mode 100644 GUI/attachprocessdialog.cpp create mode 100644 GUI/attachprocessdialog.h create mode 100644 GUI/attachprocessdialog.ui delete mode 100644 GUI/attachtoprocessdialog.cpp delete mode 100644 GUI/attachtoprocessdialog.h delete mode 100644 GUI/attachtoprocessdialog.ui delete mode 100644 GUI/utils/windowshelpers.cpp delete mode 100644 GUI/utils/windowshelpers.h diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index c8e17de..54eafae 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -2,20 +2,15 @@ include(QtUtils) msvc_registry_search() find_qt5(Core Widgets WinExtras) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - add_executable(Textractor WIN32 main.cpp mainwindow.cpp extenwindow.cpp - attachtoprocessdialog.cpp + attachprocessdialog.cpp host/exception.cpp host/host.cpp host/textthread.cpp host/hookcode.cpp - utils/windowshelpers.cpp Textractor.rc Textractor.ico ) diff --git a/GUI/attachprocessdialog.cpp b/GUI/attachprocessdialog.cpp new file mode 100644 index 0000000..830d9ed --- /dev/null +++ b/GUI/attachprocessdialog.cpp @@ -0,0 +1,48 @@ +#include "attachprocessdialog.h" +#include + +extern const char* SELECT_PROCESS; +extern const char* ATTACH_INFO; + +AttachProcessDialog::AttachProcessDialog(QWidget *parent, std::vector> processIcons) : + QDialog(parent, Qt::WindowCloseButtonHint), + model(this) +{ + ui.setupUi(this); + setWindowTitle(SELECT_PROCESS); + ui.label->setText(ATTACH_INFO); + ui.processIdEdit->setValidator(new QIntValidator(0, INT_MAX, this)); + ui.processList->setModel(&model); + + QPixmap transparent(100, 100); + transparent.fill(QColor::fromRgba(0)); + for (const auto& [process, icon] : processIcons) + { + auto item = new QStandardItem(icon ? QIcon(QtWin::fromHICON(icon)) : transparent, process); + item->setEditable(false); + model.appendRow(item); + } + + connect(ui.buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(ui.processList, &QListView::clicked, [this](QModelIndex index) + { + selectedProcess = model.item(index.row())->text(); + }); + connect(ui.processList, &QListView::doubleClicked, [this](QModelIndex index) + { + selectedProcess = model.item(index.row())->text(); + accept(); + }); + connect(ui.processIdEdit, &QLineEdit::returnPressed, [this] + { + selectedProcess = ui.processIdEdit->text(); + accept(); + }); + +} + +QString AttachProcessDialog::SelectedProcess() +{ + return selectedProcess.isEmpty() ? ui.processIdEdit->text() : selectedProcess; +} diff --git a/GUI/attachprocessdialog.h b/GUI/attachprocessdialog.h new file mode 100644 index 0000000..9d17eb6 --- /dev/null +++ b/GUI/attachprocessdialog.h @@ -0,0 +1,17 @@ +#pragma once + +#include "qtcommon.h" +#include "ui_attachprocessdialog.h" +#include + +class AttachProcessDialog : public QDialog +{ +public: + explicit AttachProcessDialog(QWidget *parent, std::vector> processIcons); + QString SelectedProcess(); + +private: + Ui::AttachProcessDialog ui; + QStandardItemModel model; + QString selectedProcess; +}; diff --git a/GUI/attachprocessdialog.ui b/GUI/attachprocessdialog.ui new file mode 100644 index 0000000..ece348a --- /dev/null +++ b/GUI/attachprocessdialog.ui @@ -0,0 +1,41 @@ + + + AttachProcessDialog + + + Qt::WindowModal + + + + 0 + 0 + 800 + 400 + + + + + + + + + + Process id + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/GUI/attachtoprocessdialog.cpp b/GUI/attachtoprocessdialog.cpp deleted file mode 100644 index c85b9d4..0000000 --- a/GUI/attachtoprocessdialog.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "attachtoprocessdialog.h" -#include "ui_attachtoprocessdialog.h" -#include "utils/windowshelpers.h" - -#include -#include -#include - -namespace -{ - QString GetNameProcessFromIndex(const QModelIndex &index, const QVector>& data) - { - const int row = index.row(); - return data[row].first; - } -} - -AttachToProcessDialog::AttachToProcessDialog(QWidget *parent) : - QDialog(parent, Qt::WindowCloseButtonHint), - ui(new Ui::AttachToProcessDialog), - model(new QStandardItemModel(this)) -{ - ui->setupUi(this); - - const QIntValidator* validator = new QIntValidator(0, INT_MAX, this); - ui->lineEdit->setValidator(validator); -} - -AttachToProcessDialog::~AttachToProcessDialog() -{ - delete ui; -} - -void AttachToProcessDialog::setLabelText(const QString& text) -{ - ui->label->setText(text); -} - -void AttachToProcessDialog::setData(QVector>&& newData) -{ - data = std::move(newData); - selectedProcess.clear(); - std::sort(data.begin(), data.end(), [](const auto& left, const auto& right) { - return left.first < right.first; - }); - model->clear(); - for (const auto& [process, hIcon] : data) - { - QIcon icon = WindowsHepers::CreateQIconFromHIcon(hIcon); - auto* item = new QStandardItem(icon, process); - item->setEditable(false); - model->appendRow(item); - } - ui->listView->setModel(model); -} - -QString AttachToProcessDialog::getSelectedData() -{ - return selectedProcess.isEmpty() ? ui->lineEdit->text() : selectedProcess; -} - -void AttachToProcessDialog::on_buttonBox_accepted() -{ - accept(); -} - -void AttachToProcessDialog::on_buttonBox_rejected() -{ - reject(); -} - -void AttachToProcessDialog::on_listView_doubleClicked(const QModelIndex& index) -{ - selectedProcess = GetNameProcessFromIndex(index, data); - accept(); -} - -void AttachToProcessDialog::on_lineEdit_returnPressed() -{ - selectedProcess = ui->lineEdit->text(); - accept(); -} - -void AttachToProcessDialog::on_listView_clicked(const QModelIndex &index) -{ - selectedProcess = GetNameProcessFromIndex(index, data); -} - -void AttachToProcessDialog::on_lineEdit_editingFinished() -{ - selectedProcess = ui->lineEdit->text(); -} diff --git a/GUI/attachtoprocessdialog.h b/GUI/attachtoprocessdialog.h deleted file mode 100644 index 3dde6e5..0000000 --- a/GUI/attachtoprocessdialog.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef ATTACHTOPROCESSDIALOG_H -#define ATTACHTOPROCESSDIALOG_H - -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -namespace Ui { -class AttachToProcessDialog; -} -QT_END_NAMESPACE - -class QStandardItemModel; -class QModelIndex; - -class AttachToProcessDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AttachToProcessDialog(QWidget *parent = nullptr); - void setData(QVector>&& newData); - void setLabelText(const QString& text); - QString getSelectedData(); - ~AttachToProcessDialog(); - -private slots: - void on_buttonBox_accepted(); - - void on_buttonBox_rejected(); - - void on_listView_doubleClicked(const QModelIndex &index); - - void on_lineEdit_returnPressed(); - - void on_listView_clicked(const QModelIndex &index); - - void on_lineEdit_editingFinished(); - -private: - Ui::AttachToProcessDialog* ui; - QStandardItemModel* model; - QString selectedProcess; - QVector> data; -}; - -#endif // ATTACHTOPROCESSDIALOG_H \ No newline at end of file diff --git a/GUI/attachtoprocessdialog.ui b/GUI/attachtoprocessdialog.ui deleted file mode 100644 index 9f3c231..0000000 --- a/GUI/attachtoprocessdialog.ui +++ /dev/null @@ -1,52 +0,0 @@ - - - AttachToProcessDialog - - - Qt::WindowModal - - - - 0 - 0 - 813 - 426 - - - - Form - - - - - - - - TextLabel - - - - - - - PID - - - - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - diff --git a/GUI/extenwindow.h b/GUI/extenwindow.h index 7561092..0cc3d40 100644 --- a/GUI/extenwindow.h +++ b/GUI/extenwindow.h @@ -2,11 +2,6 @@ #include "qtcommon.h" -namespace Ui -{ - class ExtenWindow; -} - struct InfoForExtension { const char* name; diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 9880919..a8b9615 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -5,9 +5,7 @@ #include "extenwindow.h" #include "host/host.h" #include "host/hookcode.h" -#include "attachtoprocessdialog.h" -#include "utils/windowshelpers.h" - +#include "attachprocessdialog.h" #include #include #include @@ -32,7 +30,6 @@ extern const char* SETTINGS; extern const char* EXTENSIONS; extern const char* FONT; extern const char* SELECT_PROCESS; -extern const char* ATTACH_INFO; extern const char* SELECT_PROCESS_INFO; extern const char* FROM_COMPUTER; extern const char* PROCESSES; @@ -83,7 +80,6 @@ namespace Ui::MainWindow ui; std::atomic selectedProcessId = 0; ExtenWindow* extenWindow = nullptr; - AttachToProcessDialog* attachDialog = nullptr; std::unordered_set alreadyAttached; bool autoAttach = false, autoAttachSavedOnly = true; bool showSystemProcesses = false; @@ -157,50 +153,31 @@ namespace } void AttachProcess() - { - auto processes = GetAllProcesses(); + { QMultiHash processesMap; - QVector> dialogData; - dialogData.reserve(processes.size()); - for (auto [processId, processName] : processes) + std::vector> processIcons; + for (auto [processId, processName] : GetAllProcesses()) { if (processName && (showSystemProcesses || processName->find(L":\\Windows\\") == std::string::npos)) { - const auto& value = processName.value(); - const QFileInfo& fileInfo = QFileInfo(S(value)); - const QString& fileName = fileInfo.fileName(); + QString fileName = QFileInfo(S(processName.value())).fileName(); if (!processesMap.contains(fileName)) { - const auto icon = WindowsHepers::GetIconHandlerFromExe(value.c_str()); - dialogData.push_back({fileName, icon}); + HICON bigIcon, smallIcon; + ExtractIconExW(processName->c_str(), 0, &bigIcon, &smallIcon, 1); + processIcons.push_back({ fileName, bigIcon ? bigIcon : smallIcon }); } processesMap.insert(fileName, processId); } } - dialogData.shrink_to_fit(); - processes.clear(); + std::sort(processIcons.begin(), processIcons.end()); - attachDialog->setWindowTitle(SELECT_PROCESS); - attachDialog->setLabelText(ATTACH_INFO); - - attachDialog->setData(std::move(dialogData)); - const bool hasChosenData = attachDialog->exec() != 0; - - if (hasChosenData) + AttachProcessDialog attachProcessDialog(This, processIcons); + if (attachProcessDialog.exec()) { - const QString& process = attachDialog->getSelectedData(); - const int pid = process.toInt(nullptr, 0); - if (pid) - { - Host::InjectProcess(pid); - } - else - { - for (const auto& processId : processesMap.values(process)) - { - Host::InjectProcess(processId); - } - } + QString process = attachProcessDialog.SelectedProcess(); + if (int processId = process.toInt(nullptr, 0)) Host::InjectProcess(processId); + else for (int processId : processesMap.values(process)) Host::InjectProcess(processId); } } @@ -636,7 +613,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) This = this; ui.setupUi(this); extenWindow = new ExtenWindow(this); - attachDialog = new AttachToProcessDialog(this); for (auto [text, slot] : Array{ { ATTACH, AttachProcess }, { LAUNCH, LaunchProcess }, diff --git a/GUI/utils/windowshelpers.cpp b/GUI/utils/windowshelpers.cpp deleted file mode 100644 index 2d657d9..0000000 --- a/GUI/utils/windowshelpers.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "windowshelpers.h" - -#include -#include -#include -#include -#include - -#include - -namespace WindowsHepers { - HICON GetIconHandlerFromExe(const wchar_t* const filePath) - { - HICON bigIcon; - HICON smallIcon; - ExtractIconEx(filePath, 0, &bigIcon, &smallIcon, 1); - return bigIcon != 0 ? bigIcon : smallIcon; - } - - QIcon CreateQIconFromHIcon(const HICON hIcon) - { - if (hIcon) - { - const QPixmap& pixmap = QtWin::fromHICON(hIcon); - return QIcon(pixmap); - } - const QStyle* style = QApplication::style(); - return style->standardIcon(QStyle::SP_ComputerIcon); - } -} diff --git a/GUI/utils/windowshelpers.h b/GUI/utils/windowshelpers.h deleted file mode 100644 index 405d4fe..0000000 --- a/GUI/utils/windowshelpers.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include -class QIcon; -namespace WindowsHepers -{ - HICON GetIconHandlerFromExe(const wchar_t* const filePath); - QIcon CreateQIconFromHIcon(const HICON hIcon); -} From e0981f89e89cdbafcbbaa7d45f062652f1ca4b03 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 5 Jun 2021 17:10:24 -0600 Subject: [PATCH 5/9] add new runtime file --- deploy.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy.ps1 b/deploy.ps1 index e3daffe..4d301a0 100644 --- a/deploy.ps1 +++ b/deploy.ps1 @@ -65,7 +65,6 @@ foreach ($language in @{ &"C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe" sign /a /v /t "http://timestamp.digicert.com" /fd SHA256 @(dir "$folder\**\*"); } - rm -Force -Recurse -Verbose "Runtime"; mkdir -Force -Verbose "Runtime"; foreach ($arch in @("x86", "x64")) @@ -78,6 +77,7 @@ foreach ($arch in @("x86", "x64")) "Qt5Gui.dll", "Qt5Network.dll", "Qt5WebSockets.dll", + "Qt5WinExtras.dll" "Qt5Widgets.dll", "platforms", "styles" From 7425ae770f405772b75ccb87e28afd6982373604 Mon Sep 17 00:00:00 2001 From: Blu3train Date: Fri, 14 May 2021 19:07:23 +0200 Subject: [PATCH 6/9] DevTools Systran Translate --- extensions/devtoolssystrantranslate.cpp | 160 ++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 extensions/devtoolssystrantranslate.cpp diff --git a/extensions/devtoolssystrantranslate.cpp b/extensions/devtoolssystrantranslate.cpp new file mode 100644 index 0000000..5afb98b --- /dev/null +++ b/extensions/devtoolssystrantranslate.cpp @@ -0,0 +1,160 @@ +#include "qtcommon.h" +#include "devtools.h" +#include +#include +#include + +extern const wchar_t* TRANSLATION_ERROR; +extern const char* CHROME_LOCATION; +extern const char* START_DEVTOOLS; +extern const char* STOP_DEVTOOLS; +extern const char* HEADLESS_MODE; +extern const char* DEVTOOLS_STATUS; +extern const char* AUTO_START; +extern const wchar_t* ERROR_START_CHROME; + +extern Synchronized translateTo, translateFrom; +extern QFormLayout* display; +extern Settings settings; + +const char* TRANSLATION_PROVIDER = "DevTools Systran Translate"; +const char* GET_API_KEY_FROM = nullptr; +bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true; +bool firstTranslation = true; +int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; + +QStringList languages +{ + "Chinese: zh", + "Chinese (traditional): zt", + "English: en", + "French: fr", + "German: de", + "Italian: it", + "Japanese: ja", + "Korean: ko", + "Portuguese: pt", + "Spanish: es", + "Thai: th", +}; +std::wstring autoDetectLanguage = L"auto"; + +BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + { + QString chromePath = settings.value(CHROME_LOCATION).toString(); + wchar_t programFiles[MAX_PATH + 100] = {}; + if (chromePath.isEmpty()) for (auto folder : { CSIDL_PROGRAM_FILESX86, CSIDL_PROGRAM_FILES, CSIDL_LOCAL_APPDATA }) + { + SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT, programFiles); + wcscat_s(programFiles, L"/Google/Chrome/Application/chrome.exe"); + if (std::filesystem::exists(programFiles)) chromePath = S(programFiles); + } + auto chromePathEdit = new QLineEdit(chromePath); + static struct : QObject + { + bool eventFilter(QObject* object, QEvent* event) + { + if (auto mouseEvent = dynamic_cast(event)) + if (mouseEvent->button() == Qt::LeftButton) + if (QString chromePath = QFileDialog::getOpenFileName(nullptr, TRANSLATION_PROVIDER, "/", "Chrome (*.exe)"); !chromePath.isEmpty()) + ((QLineEdit*)object)->setText(chromePath); + return false; + } + } chromeSelector; + chromePathEdit->installEventFilter(&chromeSelector); + QObject::connect(chromePathEdit, &QLineEdit::textChanged, [chromePathEdit](QString path) { settings.setValue(CHROME_LOCATION, path); }); + display->addRow(CHROME_LOCATION, chromePathEdit); + auto statusLabel = new QLabel("Stopped"); + auto startButton = new QPushButton(START_DEVTOOLS), stopButton = new QPushButton(STOP_DEVTOOLS); + auto headlessCheck = new QCheckBox(); + headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool()); + QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); }); + QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck] + { + DevTools::Start( + S(chromePathEdit->text()), + [statusLabel](QString status) + { + QMetaObject::invokeMethod(statusLabel, std::bind(&QLabel::setText, statusLabel, status)); + if (status == "ConnectedState") std::thread([] + { + if (HttpRequest httpRequest{ + L"Mozilla/5.0 Textractor", + L"127.0.0.1", + L"POST", + L"/json/version", + "", + NULL, + 9222, + NULL, + WINHTTP_FLAG_ESCAPE_DISABLE + }) + if (auto userAgent = Copy(JSON::Parse(httpRequest.response)[L"User-Agent"].String())) + if (userAgent->find(L"Headless") != std::string::npos) + DevTools::SendRequest( + "Network.setUserAgentOverride", + FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L"")) + ); + }).detach(); + }, + headlessCheck->isChecked() + ); + }); + QObject::connect(stopButton, &QPushButton::clicked, &DevTools::Close); + auto buttons = new QHBoxLayout(); + buttons->addWidget(startButton); + buttons->addWidget(stopButton); + display->addRow(HEADLESS_MODE, headlessCheck); + auto autoStartCheck = new QCheckBox(); + autoStartCheck->setChecked(settings.value(AUTO_START, false).toBool()); + QObject::connect(autoStartCheck, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); }); + display->addRow(AUTO_START, autoStartCheck); + display->addRow(buttons); + statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); + display->addRow(DEVTOOLS_STATUS, statusLabel); + if (autoStartCheck->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection); + } + break; + case DLL_PROCESS_DETACH: + { + DevTools::Close(); + } + break; + } + return TRUE; +} + +std::pair Translate(const std::wstring& text) +{ + if (!DevTools::Connected()) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, ERROR_START_CHROME) }; + // DevTools can't handle concurrent translations yet + static std::mutex translationMutex; + std::scoped_lock lock(translationMutex); + if (firstTranslation) { + firstTranslation = false; + DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://translate.systran.net"})")); + for (int retry = 0; ++retry < 100; Sleep(100)) + if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", + LR"({"expression":"document.querySelector('#translationTools').innerHTML.trim() ","returnByValue":true})" + )[L"result"][L"value"].String())) if (!translation->empty()) break; + DevTools::SendRequest("Runtime.evaluate", FormatString(LR"({"expression":" + //document.waitForSelector('#target-dummydiv'); + document.querySelector('#cookieChoiceDismiss').click(); + "})")); + } + + DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://translate.systran.net/?&source=%s&target=%s&input=%s"})", translateFrom.Copy(), translateTo.Copy(), Escape(text))); + + for (int retry = 0; ++retry < 100; Sleep(100)) + if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", + LR"({"expression":"document.querySelector('#outputEditor').textContent.trim() ","returnByValue":true})" + )[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() }; + if (auto errorMessage = Copy(DevTools::SendRequest("Runtime.evaluate", + LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})" + )[L"result"][L"value"].String())) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) }; + return { false, TRANSLATION_ERROR }; +} From 9e0c95be98c3be2290c67737032a4a34fc08b68a Mon Sep 17 00:00:00 2001 From: Blu3train Date: Fri, 14 May 2021 19:56:09 +0200 Subject: [PATCH 7/9] Updated CMakeLists.txt and deploy.ps1 files --- deploy.ps1 | 2 ++ extensions/CMakeLists.txt | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/deploy.ps1 b/deploy.ps1 index 4d301a0..6b05dd2 100644 --- a/deploy.ps1 +++ b/deploy.ps1 @@ -45,6 +45,8 @@ foreach ($language in @{ "Copy to Clipboard", "DeepL Translate", "DevTools DeepL Translate", + "DevTools Google Translate", + "DevTools Systran Translate", "Extra Newlines", "Extra Window", "Google Translate", diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index e4a5232..bbddf5f 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -7,6 +7,8 @@ add_library(Bing\ Translate MODULE bingtranslate.cpp translatewrapper.cpp networ add_library(Copy\ to\ Clipboard MODULE copyclipboard.cpp extensionimpl.cpp) add_library(DeepL\ Translate MODULE deepltranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) add_library(DevTools\ DeepL\ Translate MODULE devtoolsdeepltranslate.cpp devtools.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) +add_library(DevTools\ Google\ Translate MODULE devtoolsgoogletranslate.cpp devtools.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) +add_library(DevTools\ Systran\ Translate MODULE devtoolssystrantranslate.cpp devtools.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) add_library(Extra\ Newlines MODULE extranewlines.cpp extensionimpl.cpp) add_library(Extra\ Window MODULE extrawindow.cpp extensionimpl.cpp) add_library(Google\ Translate MODULE googletranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) @@ -24,6 +26,8 @@ target_precompile_headers(Bing\ Translate REUSE_FROM pch) target_precompile_headers(Copy\ to\ Clipboard REUSE_FROM pch) target_precompile_headers(DeepL\ Translate REUSE_FROM pch) target_precompile_headers(DevTools\ DeepL\ Translate REUSE_FROM pch) +target_precompile_headers(DevTools\ Google\ Translate REUSE_FROM pch) +target_precompile_headers(DevTools\ Systran\ Translate REUSE_FROM pch) target_precompile_headers(Extra\ Newlines REUSE_FROM pch) target_precompile_headers(Extra\ Window REUSE_FROM pch) target_precompile_headers(Google\ Translate REUSE_FROM pch) @@ -40,6 +44,8 @@ target_precompile_headers(Thread\ Linker REUSE_FROM pch) target_link_libraries(Bing\ Translate winhttp Qt5::Widgets) target_link_libraries(DeepL\ Translate winhttp Qt5::Widgets) target_link_libraries(DevTools\ DeepL\ Translate shell32 winhttp Qt5::Widgets Qt5::WebSockets) +target_link_libraries(DevTools\ Google\ Translate shell32 winhttp Qt5::Widgets Qt5::WebSockets) +target_link_libraries(DevTools\ Systran\ Translate shell32 winhttp Qt5::Widgets Qt5::WebSockets) target_link_libraries(Extra\ Window Qt5::Widgets) target_link_libraries(Google\ Translate winhttp Qt5::Widgets) target_link_libraries(Lua lua53 Qt5::Widgets) @@ -56,4 +62,16 @@ if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSockets.dll AND NOT EXISTS COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/DevTools\ DeepL\ Translate.dll" ) + add_custom_command(TARGET DevTools\ Google\ Translate + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" + COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin + COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/DevTools\ Google\ Translate.dll" + ) + add_custom_command(TARGET DevTools\ Systran\ Translate + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" + COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin + COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/DevTools\ Systran\ Translate.dll" + ) endif() From 89198fd4fe09360484e592d0f1e63f307f2c6708 Mon Sep 17 00:00:00 2001 From: Blu3train Date: Fri, 14 May 2021 22:26:46 +0200 Subject: [PATCH 8/9] Updated CMakeLists.txt and deploy.ps1 files --- deploy.ps1 | 1 - extensions/CMakeLists.txt | 9 --------- 2 files changed, 10 deletions(-) diff --git a/deploy.ps1 b/deploy.ps1 index 6b05dd2..a0def0e 100644 --- a/deploy.ps1 +++ b/deploy.ps1 @@ -45,7 +45,6 @@ foreach ($language in @{ "Copy to Clipboard", "DeepL Translate", "DevTools DeepL Translate", - "DevTools Google Translate", "DevTools Systran Translate", "Extra Newlines", "Extra Window", diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index bbddf5f..a194e6d 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -7,7 +7,6 @@ add_library(Bing\ Translate MODULE bingtranslate.cpp translatewrapper.cpp networ add_library(Copy\ to\ Clipboard MODULE copyclipboard.cpp extensionimpl.cpp) add_library(DeepL\ Translate MODULE deepltranslate.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) add_library(DevTools\ DeepL\ Translate MODULE devtoolsdeepltranslate.cpp devtools.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) -add_library(DevTools\ Google\ Translate MODULE devtoolsgoogletranslate.cpp devtools.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) add_library(DevTools\ Systran\ Translate MODULE devtoolssystrantranslate.cpp devtools.cpp translatewrapper.cpp network.cpp extensionimpl.cpp) add_library(Extra\ Newlines MODULE extranewlines.cpp extensionimpl.cpp) add_library(Extra\ Window MODULE extrawindow.cpp extensionimpl.cpp) @@ -26,7 +25,6 @@ target_precompile_headers(Bing\ Translate REUSE_FROM pch) target_precompile_headers(Copy\ to\ Clipboard REUSE_FROM pch) target_precompile_headers(DeepL\ Translate REUSE_FROM pch) target_precompile_headers(DevTools\ DeepL\ Translate REUSE_FROM pch) -target_precompile_headers(DevTools\ Google\ Translate REUSE_FROM pch) target_precompile_headers(DevTools\ Systran\ Translate REUSE_FROM pch) target_precompile_headers(Extra\ Newlines REUSE_FROM pch) target_precompile_headers(Extra\ Window REUSE_FROM pch) @@ -44,7 +42,6 @@ target_precompile_headers(Thread\ Linker REUSE_FROM pch) target_link_libraries(Bing\ Translate winhttp Qt5::Widgets) target_link_libraries(DeepL\ Translate winhttp Qt5::Widgets) target_link_libraries(DevTools\ DeepL\ Translate shell32 winhttp Qt5::Widgets Qt5::WebSockets) -target_link_libraries(DevTools\ Google\ Translate shell32 winhttp Qt5::Widgets Qt5::WebSockets) target_link_libraries(DevTools\ Systran\ Translate shell32 winhttp Qt5::Widgets Qt5::WebSockets) target_link_libraries(Extra\ Window Qt5::Widgets) target_link_libraries(Google\ Translate winhttp Qt5::Widgets) @@ -62,12 +59,6 @@ if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSockets.dll AND NOT EXISTS COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/DevTools\ DeepL\ Translate.dll" ) - add_custom_command(TARGET DevTools\ Google\ Translate - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" - COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin - COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/DevTools\ Google\ Translate.dll" - ) add_custom_command(TARGET DevTools\ Systran\ Translate POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" From 2cf146652d9eff57a93e8a55e61339adb2b0e58a Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sat, 5 Jun 2021 09:36:07 -0600 Subject: [PATCH 9/9] integrate with devtools refactor and remove unneeded code --- extensions/CMakeLists.txt | 6 -- extensions/devtoolssystrantranslate.cpp | 101 +----------------------- 2 files changed, 2 insertions(+), 105 deletions(-) diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index a194e6d..fc346dc 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -59,10 +59,4 @@ if (NOT EXISTS ${CMAKE_FINAL_OUTPUT_DIRECTORY}/Qt5WebSockets.dll AND NOT EXISTS COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/DevTools\ DeepL\ Translate.dll" ) - add_custom_command(TARGET DevTools\ Systran\ Translate - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/windeployqt" - COMMAND set PATH=%PATH%$${qt5_install_prefix}/bin - COMMAND Qt5::windeployqt --dir ${CMAKE_FINAL_OUTPUT_DIRECTORY} "${CMAKE_FINAL_OUTPUT_DIRECTORY}/DevTools\ Systran\ Translate.dll" - ) endif() diff --git a/extensions/devtoolssystrantranslate.cpp b/extensions/devtoolssystrantranslate.cpp index 5afb98b..6f158c4 100644 --- a/extensions/devtoolssystrantranslate.cpp +++ b/extensions/devtoolssystrantranslate.cpp @@ -1,17 +1,8 @@ #include "qtcommon.h" #include "devtools.h" -#include -#include -#include -extern const wchar_t* TRANSLATION_ERROR; -extern const char* CHROME_LOCATION; -extern const char* START_DEVTOOLS; -extern const char* STOP_DEVTOOLS; -extern const char* HEADLESS_MODE; -extern const char* DEVTOOLS_STATUS; -extern const char* AUTO_START; extern const wchar_t* ERROR_START_CHROME; +extern const wchar_t* TRANSLATION_ERROR; extern Synchronized translateTo, translateFrom; extern QFormLayout* display; @@ -20,7 +11,6 @@ extern Settings settings; const char* TRANSLATION_PROVIDER = "DevTools Systran Translate"; const char* GET_API_KEY_FROM = nullptr; bool translateSelectedOnly = true, rateLimitAll = false, rateLimitSelected = false, useCache = true, useFilter = true; -bool firstTranslation = true; int tokenCount = 30, tokenRestoreDelay = 60000, maxSentenceSize = 2500; QStringList languages @@ -45,78 +35,7 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved { case DLL_PROCESS_ATTACH: { - QString chromePath = settings.value(CHROME_LOCATION).toString(); - wchar_t programFiles[MAX_PATH + 100] = {}; - if (chromePath.isEmpty()) for (auto folder : { CSIDL_PROGRAM_FILESX86, CSIDL_PROGRAM_FILES, CSIDL_LOCAL_APPDATA }) - { - SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT, programFiles); - wcscat_s(programFiles, L"/Google/Chrome/Application/chrome.exe"); - if (std::filesystem::exists(programFiles)) chromePath = S(programFiles); - } - auto chromePathEdit = new QLineEdit(chromePath); - static struct : QObject - { - bool eventFilter(QObject* object, QEvent* event) - { - if (auto mouseEvent = dynamic_cast(event)) - if (mouseEvent->button() == Qt::LeftButton) - if (QString chromePath = QFileDialog::getOpenFileName(nullptr, TRANSLATION_PROVIDER, "/", "Chrome (*.exe)"); !chromePath.isEmpty()) - ((QLineEdit*)object)->setText(chromePath); - return false; - } - } chromeSelector; - chromePathEdit->installEventFilter(&chromeSelector); - QObject::connect(chromePathEdit, &QLineEdit::textChanged, [chromePathEdit](QString path) { settings.setValue(CHROME_LOCATION, path); }); - display->addRow(CHROME_LOCATION, chromePathEdit); - auto statusLabel = new QLabel("Stopped"); - auto startButton = new QPushButton(START_DEVTOOLS), stopButton = new QPushButton(STOP_DEVTOOLS); - auto headlessCheck = new QCheckBox(); - headlessCheck->setChecked(settings.value(HEADLESS_MODE, true).toBool()); - QObject::connect(headlessCheck, &QCheckBox::clicked, [](bool headless) { settings.setValue(HEADLESS_MODE, headless); }); - QObject::connect(startButton, &QPushButton::clicked, [statusLabel, chromePathEdit, headlessCheck] - { - DevTools::Start( - S(chromePathEdit->text()), - [statusLabel](QString status) - { - QMetaObject::invokeMethod(statusLabel, std::bind(&QLabel::setText, statusLabel, status)); - if (status == "ConnectedState") std::thread([] - { - if (HttpRequest httpRequest{ - L"Mozilla/5.0 Textractor", - L"127.0.0.1", - L"POST", - L"/json/version", - "", - NULL, - 9222, - NULL, - WINHTTP_FLAG_ESCAPE_DISABLE - }) - if (auto userAgent = Copy(JSON::Parse(httpRequest.response)[L"User-Agent"].String())) - if (userAgent->find(L"Headless") != std::string::npos) - DevTools::SendRequest( - "Network.setUserAgentOverride", - FormatString(LR"({"userAgent":"%s"})", userAgent->replace(userAgent->find(L"Headless"), 8, L"")) - ); - }).detach(); - }, - headlessCheck->isChecked() - ); - }); - QObject::connect(stopButton, &QPushButton::clicked, &DevTools::Close); - auto buttons = new QHBoxLayout(); - buttons->addWidget(startButton); - buttons->addWidget(stopButton); - display->addRow(HEADLESS_MODE, headlessCheck); - auto autoStartCheck = new QCheckBox(); - autoStartCheck->setChecked(settings.value(AUTO_START, false).toBool()); - QObject::connect(autoStartCheck, &QCheckBox::clicked, [](bool autoStart) { settings.setValue(AUTO_START, autoStart); }); - display->addRow(AUTO_START, autoStartCheck); - display->addRow(buttons); - statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); - display->addRow(DEVTOOLS_STATUS, statusLabel); - if (autoStartCheck->isChecked()) QMetaObject::invokeMethod(startButton, &QPushButton::click, Qt::QueuedConnection); + DevTools::Start(); } break; case DLL_PROCESS_DETACH: @@ -134,27 +53,11 @@ std::pair Translate(const std::wstring& text) // DevTools can't handle concurrent translations yet static std::mutex translationMutex; std::scoped_lock lock(translationMutex); - if (firstTranslation) { - firstTranslation = false; - DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://translate.systran.net"})")); - for (int retry = 0; ++retry < 100; Sleep(100)) - if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", - LR"({"expression":"document.querySelector('#translationTools').innerHTML.trim() ","returnByValue":true})" - )[L"result"][L"value"].String())) if (!translation->empty()) break; - DevTools::SendRequest("Runtime.evaluate", FormatString(LR"({"expression":" - //document.waitForSelector('#target-dummydiv'); - document.querySelector('#cookieChoiceDismiss').click(); - "})")); - } DevTools::SendRequest("Page.navigate", FormatString(LR"({"url":"https://translate.systran.net/?&source=%s&target=%s&input=%s"})", translateFrom.Copy(), translateTo.Copy(), Escape(text))); - for (int retry = 0; ++retry < 100; Sleep(100)) if (auto translation = Copy(DevTools::SendRequest("Runtime.evaluate", LR"({"expression":"document.querySelector('#outputEditor').textContent.trim() ","returnByValue":true})" )[L"result"][L"value"].String())) if (!translation->empty()) return { true, translation.value() }; - if (auto errorMessage = Copy(DevTools::SendRequest("Runtime.evaluate", - LR"({"expression":"document.querySelector('div.lmt__system_notification').innerHTML","returnByValue":true})" - )[L"result"][L"value"].String())) return { false, FormatString(L"%s: %s", TRANSLATION_ERROR, errorMessage.value()) }; return { false, TRANSLATION_ERROR }; }