|
34 | 34 | #include <QMetaMethod> |
35 | 35 | #include <QProcess> |
36 | 36 | #include <QProgressDialog> |
| 37 | +#include <QStandardPaths> |
37 | 38 | #include <QTreeWidget> |
38 | 39 | #include <QtMath> |
39 | 40 |
|
@@ -673,6 +674,138 @@ class RDProgressDialog : public QProgressDialog |
673 | 674 | static const int maxProgress = 1000; |
674 | 675 | }; |
675 | 676 |
|
| 677 | +#if defined(Q_OS_WIN32) |
| 678 | +#include <shellapi.h> |
| 679 | +#include <windows.h> |
| 680 | +#endif |
| 681 | + |
| 682 | +bool RunProcessAsAdmin(const QString &fullExecutablePath, const QStringList ¶ms, |
| 683 | + std::function<void()> finishedCallback) |
| 684 | +{ |
| 685 | +#if defined(Q_OS_WIN32) |
| 686 | + |
| 687 | + std::wstring wideExe = fullExecutablePath.toStdWString(); |
| 688 | + std::wstring wideParams = params.join(QChar(' ')).toStdWString(); |
| 689 | + |
| 690 | + SHELLEXECUTEINFOW info = {}; |
| 691 | + info.cbSize = sizeof(info); |
| 692 | + info.fMask = SEE_MASK_NOCLOSEPROCESS; |
| 693 | + info.lpVerb = L"runas"; |
| 694 | + info.lpFile = wideExe.c_str(); |
| 695 | + info.lpParameters = wideParams.c_str(); |
| 696 | + info.nShow = SW_SHOWNORMAL; |
| 697 | + |
| 698 | + ShellExecuteExW(&info); |
| 699 | + |
| 700 | + if((uintptr_t)info.hInstApp > 32 && info.hProcess != NULL) |
| 701 | + { |
| 702 | + if(finishedCallback) |
| 703 | + { |
| 704 | + HANDLE h = info.hProcess; |
| 705 | + |
| 706 | + // do the wait on another thread |
| 707 | + LambdaThread *thread = new LambdaThread([h, finishedCallback]() { |
| 708 | + WaitForSingleObject(h, 30000); |
| 709 | + CloseHandle(h); |
| 710 | + GUIInvoke::call(finishedCallback); |
| 711 | + }); |
| 712 | + thread->selfDelete(true); |
| 713 | + thread->start(); |
| 714 | + } |
| 715 | + else |
| 716 | + { |
| 717 | + CloseHandle(info.hProcess); |
| 718 | + } |
| 719 | + |
| 720 | + return true; |
| 721 | + } |
| 722 | + |
| 723 | + return false; |
| 724 | + |
| 725 | +#else |
| 726 | + // try to find a way to run the application elevated. |
| 727 | + const QString graphicalSudo[] = { |
| 728 | + "pkexec", "kdesudo", "gksudo", "beesu", |
| 729 | + }; |
| 730 | + |
| 731 | + // if none of the graphical options, then look for sudo and either |
| 732 | + const QString termEmulator[] = { |
| 733 | + "x-terminal-emulator", "gnome-terminal", "knosole", "xterm", |
| 734 | + }; |
| 735 | + |
| 736 | + for(const QString &sudo : graphicalSudo) |
| 737 | + { |
| 738 | + QString inPath = QStandardPaths::findExecutable(sudo); |
| 739 | + |
| 740 | + // can't find in path |
| 741 | + if(inPath.isEmpty()) |
| 742 | + continue; |
| 743 | + |
| 744 | + QProcess *process = new QProcess; |
| 745 | + |
| 746 | + QStringList sudoParams; |
| 747 | + sudoParams << fullExecutablePath; |
| 748 | + for(const QString &p : params) |
| 749 | + sudoParams << p; |
| 750 | + |
| 751 | + qInfo() << "Running" << sudo << "with params" << sudoParams; |
| 752 | + |
| 753 | + // run with sudo |
| 754 | + process->start(sudo, sudoParams); |
| 755 | + |
| 756 | + // when the process exits, call the callback and delete |
| 757 | + QObject::connect(process, OverloadedSlot<int>::of(&QProcess::finished), |
| 758 | + [process, finishedCallback](int exitCode) { |
| 759 | + process->deleteLater(); |
| 760 | + GUIInvoke::call(finishedCallback); |
| 761 | + }); |
| 762 | + |
| 763 | + return true; |
| 764 | + } |
| 765 | + |
| 766 | + QString sudo = QStandardPaths::findExecutable("sudo"); |
| 767 | + |
| 768 | + if(sudo.isEmpty()) |
| 769 | + { |
| 770 | + qCritical() << "Couldn't find graphical or terminal sudo program!\n" |
| 771 | + << "Please run " << fullExecutablePath << "with args" << params << "manually."; |
| 772 | + return false; |
| 773 | + } |
| 774 | + |
| 775 | + for(const QString &term : termEmulator) |
| 776 | + { |
| 777 | + QString inPath = QStandardPaths::findExecutable(term); |
| 778 | + |
| 779 | + // can't find in path |
| 780 | + if(inPath.isEmpty()) |
| 781 | + continue; |
| 782 | + |
| 783 | + QProcess *process = new QProcess; |
| 784 | + |
| 785 | + // run terminal sudo with emulator |
| 786 | + QStringList termParams; |
| 787 | + termParams << "-e" |
| 788 | + << QString("bash -c 'sudo %1 %2'").arg(fullExecutablePath).arg(params.join(QChar(' '))); |
| 789 | + |
| 790 | + process->start(term, termParams); |
| 791 | + |
| 792 | + // when the process exits, call the callback and delete |
| 793 | + QObject::connect(process, OverloadedSlot<int>::of(&QProcess::finished), |
| 794 | + [process, finishedCallback](int exitCode) { |
| 795 | + process->deleteLater(); |
| 796 | + GUIInvoke::call(finishedCallback); |
| 797 | + }); |
| 798 | + |
| 799 | + return true; |
| 800 | + } |
| 801 | + |
| 802 | + qCritical() << "Couldn't find graphical or terminal emulator to launch sudo.\n" |
| 803 | + << "Please run " << fullExecutablePath << "with args" << params << "manually."; |
| 804 | + |
| 805 | + return false; |
| 806 | +#endif |
| 807 | +} |
| 808 | + |
676 | 809 | void ShowProgressDialog(QWidget *window, const QString &labelText, ProgressFinishedMethod finished, |
677 | 810 | ProgressUpdateMethod update) |
678 | 811 | { |
|
0 commit comments