先描述本文解决了什么问题:
类似一个辅助进程,可以控制目标进程的启动、如果目标进程已经启动那么激活到桌面最前端的功能。
本文解决的问题:
1、一个进程已当前登录用户启动目标进程,这么做是由于,当辅助进程如果以管理员权限打开,启动另一个进程的时候,默认是以管理员权限启动的,这会导致程序中获取的路径不对,影响功能的正常执行,以及其他位置问题。
2、一个进程激活Windows桌面进程,主要解决的是Windows安全机制导致的很多场景,无法完成对目标进程的激活
3、如果目标进程最小化,该如何激活
4、进程间通信,辅助进程如何通知目标进程进行激活
下面对所有问题进行详细的描述以及附上解决的源码
一、启动进程
第一个问题比较简单,直接附上源码
当前用户权限启动程序:
// 普通用户权限启动进程
#ifdef UNICODE
bool CreateProcessWithUser(const std::wstring& exePath, const std::wstring& param, bool show)
#else
bool CreateProcessWithUser(const std::string& exePath, const std::string& param, bool show)
#endif // UNICODE
{
HANDLE hToken = 0;
HANDLE hNewToken = 0;
LPVOID pEnvironment{ NULL };
bool res{ false };
int l = param.length();
#ifdef UNICODE
wchar_t* cmd = new wchar_t[l + 1];
memcpy(cmd, param.c_str(), l * sizeof(wchar_t));
cmd[l] = 0;
#else
char* cmd = new char[l + 1];
memcpy(cmd, param.c_str(), l * sizeof(char));
cmd[l] = 0;
#endif // UNICODE
do
{
if (!GetTokenWithProcessName(L"explorer.exe", hToken))
{
LOG_ERROR("GetTokenWithProcessName Error: %u", GetLastError());
break;
}
if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL/*&sa*/, SECURITY_MAX_IMPERSONATION_LEVEL, TokenPrimary, &hNewToken))
{
LOG_ERROR("DuplicateTokenEx Error: %u", GetLastError());
break;
}
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS /*| CREATE_NEW_CONSOLE*/ | CREATE_UNICODE_ENVIRONMENT;
// 检索指定用户的环境变量。然后,可以将此块传递给 CreateProcessAsUser 函数。
if (!CreateEnvironmentBlock(&pEnvironment, hNewToken, FALSE))
{
LOG_ERROR("CreateEnvironmentBlock Error: %u", GetLastError());
break;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
#ifdef UNICODE
wchar_t desktop[] = L"winsta0\\default";
#else
char desktop[] = "winsta0\\default";
#endif
si.lpDesktop = desktop;
si.dwFlags = STARTF_USESHOWWINDOW;
if (show)
{
si.wShowWindow = SW_SHOW;
}
else
{
si.wShowWindow = SW_HIDE;
}
if (!CreateProcessAsUser(hNewToken, exePath.c_str(), cmd, 0, 0, FALSE, dwCreationFlag, pEnvironment, 0, &si, &pi))
{
#ifdef UNICODE
std::string ansicmd = mm::Charset::UnicodeToANSI(cmd);
std::string ansibat = mm::Charset::UnicodeToANSI(exePath.c_str());
LOG_ERROR("CreateProcessAsUser error! LastError=%ld, %s, %s", GetLastError(), ansibat.c_str(), ansicmd.c_str());
#else
LOG_ERROR("CreateProcessAsUser error! LastError=%ld, %s, %s", GetLastError(), exePath.c_str(), cmd.c_str());
#endif // UNICODE
break;
}
res = true;
} while (0);
// 清理
delete[] cmd;
if (hToken)
{
CloseHandle(hToken);
}
if (hNewToken)
{
CloseHandle(hNewToken);
}
if (pEnvironment)
{
DestroyEnvironmentBlock(pEnvironment);
}
return res;
}
另外附上管理员权限启动程序,如果某些场景需要的话,可以参考
// 管理员权限启动进程
#ifdef UNICODE
MMSYSSHARED_EXPORT bool CreateProcessWithAdmin(const std::wstring& exe, const std::wstring& param, bool show)
#else
MMSYSSHARED_EXPORT bool CreateProcessWithAdmin(const std::string& exe, const std::string& param, bool show)
#endif // UNICODE
{
HANDLE hToken{ NULL };
HANDLE hTokenDup{ NULL };
LPVOID pEnvironment{ NULL };
bool res{ false };
#ifdef UNICODE
wchar_t* cmd = (wchar_t*)param.c_str();
#else
char* cmd = (wchar_t*)param.c_str();
#endif
do
{
if (exe.empty())
{
LOG_ERROR("exe is null!");
break;
}
if (!GetTokenWithProcessName(L"explorer.exe", hToken))
{
LOG_ERROR("GetTokenWithProcessName Error: %u", GetLastError());
break;
}
// 复制令牌,把调用方有效的所有访问权限给复制后的令牌.
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED/*TOKEN_ALL_ACCESS*/, NULL/*&sa*/, SecurityImpersonation, TokenPrimary, &hTokenDup))
{
LOG_ERROR("DuplicateTokenEx Error: %u", GetLastError());
break;
}
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
#ifdef UNICODE
wchar_t desk[]{ TEXT("WinSta0\\Default") };
#else
char desk[]{ TEXT("WinSta0\\Default") };
#endif
si.lpDesktop = desk;
if (show)
{
si.wShowWindow = SW_SHOW;
}
else
{
si.wShowWindow = SW_HIDE;
}
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi;
// 检索指定用户的环境变量。然后,可以将此块传递给 CreateProcessAsUser 函数。
if (!CreateEnvironmentBlock(&pEnvironment, hTokenDup, FALSE))
{
LOG_ERROR("CreateEnvironmentBlock Error: %u", GetLastError());
break;
}
// 缺少环境变量时某些依赖环境变量的程序打不开,或者运行不正常。
if (!CreateProcessAsUser(hTokenDup, exe.c_str(), cmd, NULL, NULL, FALSE
, NORMAL_PRIORITY_CLASS
/*| CREATE_NEW_CONSOLE */
| CREATE_UNICODE_ENVIRONMENT
, pEnvironment, NULL, &si, &pi))
{
LOG_ERROR("CreateProcessAsUser Error: %u", GetLastError());
break;
}
res = true;
} while (0);
// 清理
if (cmd)
{
delete[]cmd;
}
if (pEnvironment)
{
DestroyEnvironmentBlock(pEnvironment);
}
if(hToken)
CloseHandle(hToken);
if (hTokenDup)
CloseHandle(hTokenDup);
return res;
}
还有一个方法,根据可执行文件名字查找进程句柄
#ifdef UNICODE
bool GetTokenWithProcessName(const wchar_t* szName, HANDLE& hToken)
#else
bool GetTokenWithProcessName(const char* szName, HANDLE& hToken)
#endif // _DEBUG
{
// HANDLE hToken{ NULL };
HANDLE hProcessSnap{ NULL };
PROCESSENTRY32 pe32{ NULL };
HANDLE hProcess{ NULL };
bool res{ false };
do
{
// 多用户模式时任务管理器里可能出现多个explorer
// 需要先获取当前会话ID,再通过枚举进程,通过比较sessionID进而得到token。
//DWORD dwSessionId = WTSGetActiveConsoleSessionId();
//PWTS_PROCESS_INFO ppi = NULL;
//DWORD dwProcessCount = 0;
//if (WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1, &ppi, &dwProcessCount))
//{
// for (int i = 0; i < dwProcessCount; i++)
// {
// if (_wcsicmp(ppi[i].pProcessName, L"explorer.exe") == 0)
// {
// if (ppi[i].SessionId == dwSessionId)
// {
// break;
// }
// }
// }
// WTSFreeMemory(ppi);
//}
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (!hProcessSnap)
{
LOG_ERROR("CreateToolhelp32Snapshot error! %d", GetLastError());
break;
}
pe32.dwSize = sizeof(PROCESSENTRY32);
for (Process32First(hProcessSnap, &pe32); Process32Next(hProcessSnap, &pe32);)
{
#ifdef UNICODE
if (_wcsicmp((pe32.szExeFile), szName))
#else
if (_stricmp((pe32.szExeFile), szName))
#endif // _DEBUG
continue;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
if (!hProcess)
{
LOG_ERROR("OpenProcess error! %d", GetLastError());
break;
}
BOOL ret = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
if(!ret)
{
LOG_ERROR("OpenProcess error! %d", GetLastError());
break;
}
res = true;
break;
}
} while (0);
if (hProcessSnap)
{
CloseHandle(hProcessSnap);
}
if (hProcess)
{
CloseHandle(hProcess);
}
return res;
}
以上解决方案参考windows服务中以 管理员权限\普通权限 启动进程_createprocess 管理员权限_火苗999℃的博客-CSDN博客
二、一个进程激活任意Windows桌面进程
可以通过获取目标进程的句柄,然后给该句柄发送windows的消息,但使用过程中,发现当辅助进程在没有获取焦点的情况下,发送的激活消息是无效的,后查阅资料,是由于windows的安全机制导致的,需要使用如下的方法,才可以在任意情况下激活目标进程
#include <iostream>
#include <windows.h>
// SwitchToThisWindow 方法
void ActiveAnyWindow_SwitchToThisWindow(HWND hWnd)
{
// 第二个参数传TRUE:如果窗口被极小化了,则恢复
SwitchToThisWindow(hWnd, TRUE);
}
// SetForegroundWindow 方法
void ActiveAnyWindow_SetForegroundWindow(HWND hWnd)
{
const HWND hForeWnd = ::GetForegroundWindow();
::AttachThreadInput(::GetWindowThreadProcessId(hForeWnd, nullptr), ::GetCurrentThreadId(), TRUE);
if (!::SetForegroundWindow(hWnd))
{
std::cout << "SetForegroundWindow GLE=%d", ::GetLastError();
}
else
{
std::cout << "SetForegroundWindow OK";
}
AttachThreadInput(::GetWindowThreadProcessId(hForeWnd, nullptr), ::GetCurrentThreadId(), FALSE);
}
// BringWindowToTop 方法
void ActiveAnyWindow_BringWindowToTop(HWND hWnd)
{
const HWND hForeWnd = ::GetForegroundWindow();
::AttachThreadInput(::GetWindowThreadProcessId(hForeWnd, nullptr), ::GetCurrentThreadId(), TRUE);
if (!::BringWindowToTop(hWnd))
{
std::cout << "BringWindowToTop GLE=%d", ::GetLastError();
}
else
{
std::cout << "BringWindowToTop OK";
}
AttachThreadInput(::GetWindowThreadProcessId(hForeWnd, nullptr), ::GetCurrentThreadId(), FALSE);
}
int main()
{
::Sleep(3000);
// 先要打开系统记事本
ActiveAnyWindow_BringWindowToTop(::FindWindow(L"Notepad", nullptr));
}
注意
被 AttachThreadInput 的进程,如果是具有管理员权限的进程,则调用 AttachThreadInput 的进程也必须要有管理员权限,否则 会失败,GetLastError 返回5(拒绝访问)。
窗口句柄如何获取呢?
三个办法,一个是根据窗口的title,调用windows API
HWND hwnd = ::FindWindow(NULL, "Title");
通过进程id获取窗口句柄
HWND s_hFoundWindow = NULL;
HWND FindWindowByProcessId(DWORD dwProcessId)
{
qDebug() << "process id -> " << dwProcessId;
s_hFoundWindow = NULL;
EnumWindows(EnumWindowsProcId, (LPARAM)dwProcessId);
return s_hFoundWindow;
}
BOOL CALLBACK EnumWindowsProcId(HWND hwnd, LPARAM lParam)
{
DWORD dwID;
GetWindowThreadProcessId(hwnd, &dwID);
if (dwID == (DWORD)lParam)
{
// 找到了窗口句柄,将其保存在全局变量中
s_hFoundWindow = hwnd;
return FALSE;
}
return TRUE;
}
通过可执行文件名获取窗口句柄
HWND FindWindowByProcessName(LPCWSTR processName)
{
s_hFoundWindow = NULL;
EnumWindows(EnumWindowsProcName, (LPARAM)processName);
return s_hFoundWindow;
}
BOOL CALLBACK EnumWindowsProcName(HWND hwnd, LPARAM lParam)
{
DWORD dwID;
GetWindowThreadProcessId(hwnd, &dwID);
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwID);
if (hProcess == NULL)
{
qDebug() << "Could not open process\n";
return 1;
}
TCHAR filename[MAX_PATH];
GetModuleFileNameEx(hProcess, NULL, filename, MAX_PATH);
CloseHandle(hProcess);
QString currentFileName = QString::fromStdWString(filename);
if (currentFileName.contains(QString::fromStdString((char*)lParam), Qt::CaseInsensitive))
{
// 找到了窗口句柄,将其保存在全局变量中
s_hFoundWindow = hwnd;
return FALSE;
}
return TRUE;
}
注意,需要包含库和对应的头文件
下面是qt的pro配置,vs的话可以自行配置即可
LIBS += -lAdvapi32 -lWtsapi32 -lUserenv -lUser32
三、如果目标进程最小化
如果目标进程是最小化状态,那么在激活的时候,只是状态栏闪烁一下,并不会在窗口显示目标界面,需要给句柄进程发送点击的消息SC_RESTORE
结合上面的激活功能,代码如下
void ActiveAnyWindow_BringWindowToTop(HWND hWnd)
{
const HWND hForeWnd = ::GetForegroundWindow();
::AttachThreadInput(::GetWindowThreadProcessId(hForeWnd, nullptr), ::GetCurrentThreadId(), TRUE);
if (!::BringWindowToTop(hWnd)) {
qDebug() << "BringWindowToTop GLE=%d", ::GetLastError();
} else {
qDebug() << "BringWindowToTop OK";
}
AttachThreadInput(::GetWindowThreadProcessId(hForeWnd, nullptr), ::GetCurrentThreadId(), FALSE);
UINT msg = WM_SYSCOMMAND;
WPARAM wParam = SC_RESTORE;
LPARAM lParam = 0;
PostMessage(hWnd, msg, wParam, lParam);
}
四、进程间通信
为什么会需要用进程间通信呢,因为某些程序,通过进程id或者进程名称获取的窗口句柄,并不是其显示的窗口,这样会导致并不能成功激活,即使可以通过窗口标题获取窗口句柄,但是有些程序的窗口标题是动态变化的
所以可以通过进程间通讯的方式,当需要激活的时候,告诉目标进程,目标进程按照上面说的方法,调用ActiveAnyWindow_BringWindowToTop即可
进程间通讯可考虑的有三种
1.通过windows消息WM_COPYDATA,该方案的有点事比较安全,缺点是不够灵活,需要获取窗口句柄,又回到之前的问题了
2.通过共享内存,该方案的优点是比较灵活,不需要获取窗口句柄,目标进程在需要的地方,监视共享内存锁即可,缺点是安全性不高,会有崩溃等问题的产生
3.通过socket通信,比较推荐,该方案比较安全而且比较灵活
前两种方案感兴趣的可自行查阅相关资料,本文主要对socket通信做个详细描述
主要是借助qt的LocalSocket
LocalSocketClient.h
#ifndef LOCALCLIENT_H
#define LOCALCLIENT_H
#include <QObject>
#include <QLocalSocket>
class LocalSocketClient : public QObject
{
Q_OBJECT
public:
explicit LocalSocketClient(QObject *parent = 0);
~LocalSocketClient();
void connectServer();
bool isConnectServer();
void sendData(const QString &data);
private:
QLocalSocket *socket;
private slots:
void readData();
void displayError(QLocalSocket::LocalSocketError socketError);
};
#endif // LOCALCLIENT_H
LocalSocketClient.cpp
#include <QDataStream>
//#include <syslog.h>
#include "LocalSocketClient.h"
#define SERVER_NAME "InterProcessCom"
#define IPC_RECV_BUF_LEN (1024)
LocalSocketClient::LocalSocketClient(QObject *parent) : QObject(parent)
{
socket = new QLocalSocket(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(readData()));
connect(socket, SIGNAL(error(QLocalSocket::LocalSocketError)),
this, SLOT(displayError(QLocalSocket::LocalSocketError)));
socket->abort();
socket->connectToServer(SERVER_NAME);
}
LocalSocketClient::~LocalSocketClient()
{
if(socket != NULL)
{
socket->abort();
delete socket;
socket = NULL;
}
}
void LocalSocketClient::connectServer()
{
socket->connectToServer(SERVER_NAME);
}
bool LocalSocketClient::isConnectServer()
{
return socket->state() == QLocalSocket::ConnectedState;
}
void LocalSocketClient::sendData(const QString &data)
{
socket->write(data.toStdString().c_str());
}
void LocalSocketClient::readData()
{
// socket->read();
}
void LocalSocketClient::displayError(QLocalSocket::LocalSocketError socketError)
{
switch (socketError) {
case QLocalSocket::ServerNotFoundError:
break;
case QLocalSocket::ConnectionRefusedError:
break;
case QLocalSocket::PeerClosedError:
break;
default:
break;
}
}
LocalSocketServer.h
#ifndef LOCALSERVER_H
#define LOCALSERVER_H
#include <QObject>
#include <QLocalServer>
#include <QLocalSocket>
class LocalSocketServer : public QObject
{
Q_OBJECT
public:
explicit LocalSocketServer(QObject *parent = 0);
~LocalSocketServer();
signals:
void receiveData(const QString &data);
private slots:
void processNewConnection();
void disconnect();
void readData();
private:
QLocalServer *server;
QVector<QLocalSocket *> localSocketList;
};
#endif // LOCALSERVER_H
LocalSocketServer.cpp文章来源:https://www.toymoban.com/news/detail-545507.html
#include <QDebug>
#include <QFileInfo>
#include <QDataStream>
//#include <syslog.h>
#include "LocalSocketServer.h"
#define SERVER_NAME "InterProcessCom"
#define IPC_RECV_BUF_LEN (1024)
LocalSocketServer::LocalSocketServer(QObject *parent) : QObject(parent)
{
qDebug() << "LocalServer construct";
server = new QLocalServer(this);
QLocalServer::removeServer(SERVER_NAME);
if(!server->listen(SERVER_NAME))
{
qDebug() << "server listen failed!" << server->errorString();
}
connect(server, SIGNAL(newConnection()), this, SLOT(processNewConnection()));
}
LocalSocketServer::~LocalSocketServer()
{
if(server != NULL)
{
server->close();
delete server;
server = NULL;
}
}
void LocalSocketServer::processNewConnection()
{
qDebug() << "LocalSocketServer processNewConnection";
QLocalSocket *clientConnection = server->nextPendingConnection();
localSocketList.append(clientConnection);
connect(clientConnection, SIGNAL(readyRead()),
this, SLOT(readData()));
connect(clientConnection, SIGNAL(disconnected()),
this, SLOT(disconnect()));
}
void LocalSocketServer::disconnect()
{
for(int i = 0; i < localSocketList.size(); i++)
{
if(localSocketList.at(i)->state() != QLocalSocket::ConnectedState)
{
localSocketList.at(i)->deleteLater();
localSocketList.removeAt(i);
}
}
qDebug() << "after disconnect localSocketList size:" << localSocketList.size();
}
void LocalSocketServer::readData()
{
qDebug() << " data come ";
QLocalSocket *local = static_cast<QLocalSocket*>(sender());
if (!local)
return ;
receiveData(local->readAll());
}
需要注意的是,在使用的时候,发送前,需要先判断是否连接,未连接的时候先连接,同时需要注意,即使连接了,如果目标进程未启动也是会有异常,需要再次进行判断,如下文章来源地址https://www.toymoban.com/news/detail-545507.html
DWORD FindProcessId(const char *processName)
{
qDebug() << QString::fromStdString(processName);
// strip path
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE)
return 0;
Process32First(processesSnapshot, &processInfo);
if (!strcmp(processName, QString::fromStdWString(processInfo.szExeFile).toStdString().c_str()))
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
while (Process32Next(processesSnapshot, &processInfo))
{
if (!strcmp(processName, QString::fromStdWString(processInfo.szExeFile).toStdString().c_str()))
{
CloseHandle(processesSnapshot);
return processInfo.th32ProcessID;
}
}
CloseHandle(processesSnapshot);
return 0;
}
DWORD processId = FindProcessId("Target.exe");
if (processId == 0) {
OpenTargetProcess();
} else {
if (!m_socketClient->isConnectServer()) {
m_socketClient->connectServer();
}
if (m_socketClient->isConnectServer()) {
m_socketClient->sendData("mydata");
}
}
到了这里,关于Windows一个进程启动或者激活其他任意一个桌面进程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!