QT托盘小图标多菜单片段-ReviveOverlay

以ReviveOverlay为例分析:

trayiconcontroller.h

#ifndef CTRAYICONCONTROLLER_H
#define CTRAYICONCONTROLLER_H

#include <QObject>
#include <QMenu>
#include <QSystemTrayIcon>

enum ETrayInfo
{
	TrayInfo_OculusLibraryNotFound,
	TrayInfo_AutoLaunchEnabled,
	TrayInfo_AutoLaunchFailed
};

class CTrayIconController : public QObject
{
	Q_OBJECT
	typedef QObject BaseClass;

public:
	static CTrayIconController *SharedInstance();

public:
	CTrayIconController();
	~CTrayIconController();

	bool Init();
	void ShowInformation(ETrayInfo info);

public slots:
	void quit();

protected slots:
	void inject();
	void patch();
	void showHelp();
	void messageClicked();

private:
	QSystemTrayIcon m_trayIcon;
	QMenu m_trayIconMenu;
	ETrayInfo m_LastInfo;

	QString openDialog();
};

#endif // CTRAYICONCONTROLLER_H



trayiconcontroller.cpp

#include "trayiconcontroller.h"
#include <windowsservices.h>
#include <qt_windows.h>
#include <winsparkle.h>

#include <QCoreApplication>
#include <QDesktopServices>
#include <QFileDialog>
#include <QFileInfo>
#include <QIcon>
#include <QProcess>
#include <QUrl>

CTrayIconController *s_pSharedTrayController = NULL;

CTrayIconController *CTrayIconController::SharedInstance()
{
	if ( !s_pSharedTrayController )
	{
		s_pSharedTrayController = new CTrayIconController();
	}
	return s_pSharedTrayController;
}

CTrayIconController::CTrayIconController()
	: BaseClass()
	, m_trayIcon(QIcon(":/revive_white.ico"))
{
	QObject::connect(&m_trayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked()));
}

CTrayIconController::~CTrayIconController()
{
}

bool CTrayIconController::Init()
{
	m_trayIconMenu.addAction("&Inject...", this, SLOT(inject()));
	m_trayIconMenu.addAction("&Patch...", this, SLOT(patch()));
	m_trayIconMenu.addAction("&Help", this, SLOT(showHelp()));
	m_trayIconMenu.addSeparator();
	m_trayIconMenu.addAction("Check for &updates", win_sparkle_check_update_with_ui);
	m_trayIconMenu.addAction("&Quit", this, SLOT(quit()));
	m_trayIcon.setContextMenu(&m_trayIconMenu);
	m_trayIcon.setToolTip("Revive Dashboard");
	m_trayIcon.show();

	return true;
}

void CTrayIconController::ShowInformation(ETrayInfo info)
{
	m_LastInfo = info;

	switch (info)
	{
		case TrayInfo_AutoLaunchEnabled:
			m_trayIcon.showMessage("Revive succesfully installed",
								   "Revive will automatically add Oculus Store games to your library while SteamVR is running.",
								   QSystemTrayIcon::Information);
		break;
		case TrayInfo_AutoLaunchFailed:
			m_trayIcon.showMessage("Revive did not start correctly",
								   "Unable to set the auto-launch flag, please report this to the Revive issue tracker.",
								   QSystemTrayIcon::Critical);
		break;
		case TrayInfo_OculusLibraryNotFound:
			m_trayIcon.showMessage("Revive did not start correctly",
								   "No Oculus Library was found, please install the Oculus Software from oculus.com/setup.",
								   QSystemTrayIcon::Warning);
		break;
	}
}

void CTrayIconController::quit()
{
	m_trayIcon.setVisible(false);
	QCoreApplication::quit();
}

void CTrayIconController::inject()
{
	QString file = openDialog();
	if (file.isNull())
		return;

	QStringList args;
	args.append(file);
	QProcess::execute(QCoreApplication::applicationDirPath() + "/Revive/ReviveInjector_x64.exe", args);
}

void CTrayIconController::patch()
{
	QString file = openDialog();
	if (file.isNull())
		return;

	DWORD type;
	if (!GetBinaryType(qUtf16Printable(file), &type))
		return;

	QString dir = QCoreApplication::applicationDirPath();
	QStringList files;
	if (type == SCS_32BIT_BINARY)
	{
		files.append(dir + "/Revive/x86/LibRevive32_0_7.dll");
		files.append(dir + "/Revive/x86/LibRevive32_0_7.dll");
		files.append(dir + "/Revive/x86/openvr_api.dll");
	}
	if (type == SCS_64BIT_BINARY)
	{
		files.append(dir + "/Revive/x64/LibRevive64_0_7.dll");
		files.append(dir + "/Revive/x64/LibRevive64_0_7.dll");
		files.append(dir + "/Revive/x64/openvr_api.dll");
	}
	QStringList names = { "xinput1_3.dll", "xinput9_1_0.dll", "openvr_api.dll" };

	QFileInfo info(file);
	WindowsServices::CopyFiles(files, info.absolutePath(), names);
}

void CTrayIconController::showHelp()
{
	QDesktopServices::openUrl(QUrl("https://github.com/LibreVR/Revive/wiki"));
}

void CTrayIconController::messageClicked()
{
	switch (m_LastInfo)
	{
		case TrayInfo_AutoLaunchFailed:
			QDesktopServices::openUrl(QUrl("https://github.com/LibreVR/Revive/issues"));
		break;
		case TrayInfo_OculusLibraryNotFound:
			QDesktopServices::openUrl(QUrl("https://oculus.com/setup"));
		break;
	}
}

QString CTrayIconController::openDialog()
{
	return QFileDialog::getOpenFileName(
				nullptr, "Revive",
				QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
				"Application (*.exe)");
}


1.CTrayIconController::Init()所实现的功能如下所示:


2.m_trayIconMenu.addAction("&Inject...", this, SLOT(inject())); //添加菜单项,点击Inject...时,触发信号槽inject()。在inject()中打开对话框openDialog如下:




再选中一个oculus的游戏exe打开,通过openDialog返回文件名,通过init中的QProcess::execute(QCoreApplication::applicationDirPath() + "/Revive/ReviveInjector_x64.exe", args);启动该程序。Inject的游戏必须为不是从Oculus Home下载的独立游戏。


QString CTrayIconController::openDialog()
{
	return QFileDialog::getOpenFileName(
				nullptr, "Revive",
				QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
				"Application (*.exe)");
}



QStandardPaths::writableLocation(QStandardPaths::DesktopLocation):权限可写的默认目录位置为QStandardPaths::DesktopLocation,也就是桌面位置。

文件名后缀为:Application (*.exe)

对话框左上角名为Revive

3.m_trayIconMenu.addAction("&Patch...", this, SLOT(patch()));:同上一样,点击Patch...触发信号槽patch()。

patch()中,如2一样,先openDialog,选中一个文件并返回文件名,之后通过GetBinaryType判断该文件是否为可执行文件,并获取其类型:为32位还是64位。

另外再获取当前应用程序所在目录路径QString dir = QCoreApplication::applicationDirPath();如下:



之后files.append将需要打包的dll文件路径与前面获取的路径拼接起来。



进入CopyFiles内:

1. Microsoft::WRL::ComPtr<IFileOperation> pfo;声明一个指向模板参数为IFileOperation接口的智能指针pfo。ComPtr类为潜在的接口指针自动维护一个索引数

,且在索引数为0时自动释放该接口。IFileOperation是一个COM库接口。

2. HRESULT hr = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pfo));用一个指定的CLSID_FileOperation创建一个对象IFileOperation,pfo返回。

3.QString dstDir = QDir::toNativeSeparators(destination);将路径中的左斜杠\替换为右斜杠/

4.hr = SHCreateItemFromParsingName(qUtf16Printable(dstDir),
NULL,
IID_PPV_ARGS(&psiTo));

根据所提供的源路径dstDir创建一个IShellItem。

5.hr = pfo->CopyItem(
psiFrom.Get(),
psiTo.Get(),
name->isNull() ? nullptr : qUtf16Printable(*name),
nullptr);

将psiFrom.Get()复制到psiTo.Get()内,重命名为name.


6.hr = pfo->PerformOperations();


为什么要patch?

是因为通过Steam平台发布的Oculus游戏不能inject,只有通过patch将两个dll文件:文件LibRevive64_0_7.dll以及openvr_api.dll打包并

重名字为"xinput1_3.dll", "xinput9_1_0.dll", "openvr_api.dll",拷贝到Oculus游戏exe所在目录,这可保证当通过Steam启动Oculus游戏时,一定能够

加载Revive。比如最终打包的文件拷贝位置:



4.void CTrayIconController::showHelp()  对应菜单项Help

{
QDesktopServices::openUrl(QUrl("https://github.com/LibreVR/Revive/wiki"));
}

在用户桌面环境打开给定的URL地址所指向的网页。


5.m_trayIconMenu.addAction("Check for &updates", win_sparkle_check_update_with_ui);对应菜单Check for update,点击后弹出软件更新窗口






winsparkle 参考网页


后面的quit类似

m_trayIcon.setToolTip("Revive Dashboard"); 设置显示名字,当鼠标移动到上面是显示的,如下:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值