- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
挂起与恢复进程是指暂停或恢复进程的工作状态,以达到一定的控制和管理效果。在 Windows 操作系统中,可以使用系统提供的函数实现进程的挂起和恢复,以达到对进程的控制和调度。需要注意,过度使用进程挂起/恢复操作可能会造成系统性能的降低,导致死锁等问题,因此在使用时应该谨慎而慎重。同时,通过和其他进程之间协同工作,也可以通过更加灵活的方式,实现进程的协调、交互等相应的功能,从而实现更加高效和可靠的进程管理.
要实现挂起进程,首先我们需要实现挂起线程,因为挂起进程的实现原理是通过调用 SuspendThread 函数循环将进程内的所有线程全部挂起后实现的,而要实现挂起线程则我们需要先确定指定进程内的线程信息,要实现枚举进程内的线程信息则可以通过以下几个步骤实现.
首先通过 CreateToolhelp32Snapshot 得到当前系统下所有的进程快照,并通过遍历进程的方式寻找是否符合我们所需要枚举的进程名,如果是则调用 CreateToolhelp32Snapshot 并通过传入 TH32CS_SNAPTHREAD 代表枚举线程,通过循环的方式遍历进程内的线程,每次通过调用 OpenThread 打开线程,并调用 ZwQueryInformationThread 查询该线程的入口信息以及线程所在的模块信息,最后以此输出即可得到当前进程内的所有线程信息.
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <Psapi.h>
using namespace std;
typedef enum _THREADINFOCLASS
{
ThreadBasicInformation,
ThreadTimes,
ThreadPriority,
ThreadBasePriority,
ThreadAffinityMask,
ThreadImpersonationToken,
ThreadDescriptorTableEntry,
ThreadEnableAlignmentFaultFixup,
ThreadEventPair_Reusable,
ThreadQuerySetWin32StartAddress,
ThreadZeroTlsCell,
ThreadPerformanceCount,
ThreadAmILastThread,
ThreadIdealProcessor,
ThreadPriorityBoost,
ThreadSetTlsArrayAddress,
ThreadIsIoPending,
ThreadHideFromDebugger,
ThreadBreakOnTermination,
MaxThreadInfoClass
}THREADINFOCLASS;
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
}CLIENT_ID;
typedef struct _THREAD_BASIC_INFORMATION
{
LONG ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
LONG AffinityMask;
LONG Priority;
LONG BasePriority;
}THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
extern "C" LONG(__stdcall * ZwQueryInformationThread)
(
IN HANDLE ThreadHandle,
IN THREADINFOCLASS ThreadInformationClass,
OUT PVOID ThreadInformation,
IN ULONG ThreadInformationLength,
OUT PULONG ReturnLength OPTIONAL
) = NULL;
// 枚举进程内的线程
BOOL EnumThread(char *ProcessName)
{
// 进程快照句柄
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 process = { sizeof(PROCESSENTRY32) };
// 遍历进程
while (Process32Next(hProcessSnap, &process))
{
// char* 转 string
string s_szExeFile = process.szExeFile;
if (s_szExeFile == ProcessName)
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
// 创建线程快照
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
// 为快照分配内存空间
te32.dwSize = sizeof(THREADENTRY32);
// 获取第一个线程的信息
if (!Thread32First(hThreadSnap, &te32))
{
return FALSE;
}
// 遍历线程
while (Thread32Next(hThreadSnap, &te32))
{
// 判断线程是否属于本进程
if (te32.th32OwnerProcessID == process.th32ProcessID)
{
// 打开线程
HANDLE hThread = OpenThread(
THREAD_ALL_ACCESS, // 访问权限
FALSE, // 由此线程创建的进程不继承线程的句柄
te32.th32ThreadID // 线程 ID
);
if (hThread == NULL)
{
return FALSE;
}
// 将区域设置设置为从操作系统获取的ANSI代码页
setlocale(LC_ALL, ".ACP");
// 获取 ntdll.dll 的模块句柄
HINSTANCE hNTDLL = ::GetModuleHandle("ntdll");
// 从 ntdll.dll 中取出 ZwQueryInformationThread
(FARPROC&)ZwQueryInformationThread = GetProcAddress(hNTDLL, "ZwQueryInformationThread");
// 获取线程入口地址
PVOID startaddr; // 用来接收线程入口地址
ZwQueryInformationThread(
hThread, // 线程句柄
ThreadQuerySetWin32StartAddress, // 线程信息类型 ThreadQuerySetWin32StartAddress 线程入口地址
&startaddr, // 指向缓冲区的指针
sizeof(startaddr), // 缓冲区的大小
NULL
);
// 获取线程所在模块
THREAD_BASIC_INFORMATION tbi; // _THREAD_BASIC_INFORMATION 结构体对象
TCHAR modname[MAX_PATH]; // 用来接收模块全路径
ZwQueryInformationThread(
hThread, // 线程句柄
ThreadBasicInformation, // 线程信息类型,ThreadBasicInformation :线程基本信息
&tbi, // 指向缓冲区的指针
sizeof(tbi), // 缓冲区的大小
NULL
);
// 检查入口地址是否位于某模块中
GetMappedFileName(
OpenProcess( // 进程句柄
PROCESS_ALL_ACCESS, // 访问权限
FALSE, // 由此线程创建的进程不继承线程的句柄
(DWORD)tbi.ClientId.UniqueProcess // 唯一进程 ID
),
startaddr, // 要检查的地址
modname, // 用来接收模块名的指针
MAX_PATH // 缓冲区大小
);
std::cout << "线程ID: " << te32.th32ThreadID << " 线程入口: " << startaddr << " 所在模块: " << modname << std::endl;
}
}
}
}
}
int main(int argc, char* argv[])
{
EnumThread("lyshark.exe");
system("pause");
return 0;
}
读者可自行运行上述代码片段,即可枚举出当前运行进程 lyshark.exe 中所有的后动线程信息,如下图所示; 。
当我们能够得到当前进程内的线程信息后,接下来就是实现如何挂起或恢复进程内的特定线程,挂起线程可以使用 SuspendThread 其函数声明如下:
DWORD SuspendThread(
HANDLE hThread
);
其中, hThread 是一个指向线程句柄的指针,指向要挂起的线程的句柄,该函数返回挂起前线程的线程计数器值,表示被挂起线程在挂起前还未执行的指令数目.
可以多次调用 SuspendThread 函数将同一个线程进行多次挂起,每次返回被挂起前线程的线程计数器值,每调用一次则会阻塞该线程,其状态会变为挂起状态。当该线程被 ResumeThread 恢复时,它将继续从上次挂起时的位置开始执行.
ResumeThread 函数声明如下:
DWORD ResumeThread(
HANDLE hThread
);
其中, hThread 是线程句柄,指向要恢复的线程的句柄.
调用 ResumeThread 函数可以让一个被挂起的线程从上次挂起的位置开始继续执行,函数返回值是被恢复的线程的先前挂起次数。当被恢复的线程的挂起计数器归零时,其状态将自动变为非挂起状态,并开始继续执行.
当有了上述两个函数的支持那么挂起线程将变得很容易实现了,首先后去所有进程快照,接着就是直接打开 OpenThread() 符合要求的线程,此时只需要调用 SuspendThread(hThread) 即可挂起一个线程,调用 ResumeThread(hThread) 则可以恢复一个线程,具体实现代码如下所示; 。
#include <windows.h>
#include <stdio.h>
#include <TlHelp32.h>
int Start_Stop_Thread(DWORD Pid, DWORD ThreadID, BOOL flag)
{
THREADENTRY32 te32 = { 0 };
te32.dwSize = sizeof(THREADENTRY32);
// 获取全部线程快照
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (INVALID_HANDLE_VALUE != hThreadSnap)
{
// 获取快照中第一条信息
BOOL bRet = Thread32First(hThreadSnap, &te32);
while (bRet)
{
// 只过滤出 pid 里面的线程
if (Pid == te32.th32OwnerProcessID)
{
// 判断是否为ThreadID,暂停指定的TID
if (ThreadID == te32.th32ThreadID)
{
// 打开线程
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
if (flag == TRUE)
{
ResumeThread(hThread); // 恢复线程
}
else
{
SuspendThread(hThread); // 暂停线程
}
CloseHandle(hThreadSnap);
}
}
// 获取快照中下一条信息
bRet = Thread32Next(hThreadSnap, &te32);
}
return 0;
}
return -1;
}
int main(int argc, char* argv[])
{
// 暂停或恢复进程ID = 4204 里面的线程ID = 10056
int ret = Start_Stop_Thread(4204, 10056, TRUE); // TRUE = 恢复线程 FALSE = 挂起线程
printf("状态: %d \n", ret);
system("pause");
return 0;
}
当有了上述功能的支持以后,那么实现挂起进程将变得很容易,读者只需要在特定一个进程内枚举出所有的活动线程,并通过循环的方式逐个挂起即可实现挂起整个进程的效果,这段完整代码如下所示; 。
#include <windows.h>
#include <iostream>
#include <tlhelp32.h>
#include <Psapi.h>
#pragma comment(lib,"psapi.lib")
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#define SystemProcessesAndThreadsInformation 5 // 功能号
typedef DWORD(WINAPI* PQUERYSYSTEM)(UINT, PVOID, DWORD, PDWORD);
// 线程状态的枚举常量
typedef enum _THREAD_STATE
{
StateInitialized, // 初始化状态
StateReady, // 准备状态
StateRunning, // 运行状态
StateStandby,
StateTerminated, // 关闭
StateWait, // 等待
StateTransition, // 切换
StateUnknown
}THREAD_STATE;
// 线程处于等待的原因的枚举常量
typedef enum _KWAIT_REASON
{
Executive,
FreePage,
PageIn,
PoolAllocation,
DelayExecution,
Suspended,
UserRequest,
WrExecutive,
WrFreePage,
WrPageIn,
WrPoolAllocation,
WrDelayExecution,
WrSuspended,
WrUserRequest,
WrEventPair,
WrQueue,
WrLpcReceive,
WrLpcReply,
WrVirtualMemory,
WrPageOut,
WrRendezvous,
Spare2,
Spare3,
Spare4,
Spare5,
Spare6,
WrKernel,
MaximumWaitReason
}KWAIT_REASON;
typedef LONG NTSTATUS;
typedef LONG KPRIORITY;
typedef struct _CLIENT_ID
{
DWORD UniqueProcess;
DWORD UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
typedef struct _VM_COUNTERS
{
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
} VM_COUNTERS;
// 线程信息结构体
typedef struct _SYSTEM_THREAD_INFORMATION
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitchCount;
LONG State;// 状态,是THREAD_STATE枚举类型中的一个值
LONG WaitReason;//等待原因, KWAIT_REASON中的一个值
} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
// 进程信息结构体
typedef struct _SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryDelta; // 指向下一个结构体的指针
ULONG ThreadCount; // 本进程的总线程数
ULONG Reserved1[6]; // 保留
LARGE_INTEGER CreateTime; // 进程的创建时间
LARGE_INTEGER UserTime; // 在用户层的使用时间
LARGE_INTEGER KernelTime; // 在内核层的使用时间
UNICODE_STRING ProcessName; // 进程名
KPRIORITY BasePriority;
ULONG ProcessId; // 进程ID
ULONG InheritedFromProcessId;
ULONG HandleCount; // 进程的句柄总数
ULONG Reserved2[2]; // 保留
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters;
SYSTEM_THREAD_INFORMATION Threads[5]; // 子线程信息数组
}SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;
// 获取线程是被是否被挂起 1=表示线程被挂起 0=表示线程正常 -1=未知状态
int IsThreadSuspend(DWORD dwProcessID, DWORD dwThreadID)
{
int nRet = 0;
NTSTATUS Status = 0;
PQUERYSYSTEM NtQuerySystemInformation = NULL;
PSYSTEM_PROCESS_INFORMATION pInfo = { 0 };
// 获取函数地址
NtQuerySystemInformation = (PQUERYSYSTEM) GetProcAddress(LoadLibrary("ntdll.dll"), "NtQuerySystemInformation");
DWORD dwSize = 0;
// 获取信息所需的缓冲区大小
Status = NtQuerySystemInformation(SystemProcessesAndThreadsInformation,// 要获取的信息的类型
NULL, // 用于接收信息的缓冲区
0, // 缓冲区大小
&dwSize
);
// 申请缓冲区
char* pBuff = new char[dwSize];
pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuff;
if (pInfo == NULL)
return -1;
// 再次调用函数, 获取信息
Status = NtQuerySystemInformation(SystemProcessesAndThreadsInformation, // 要获取的信息的类型
pInfo, // 用于接收信息的缓冲区
dwSize, // 缓冲区大小
&dwSize
);
if (!NT_SUCCESS(Status))
{
/*如果函数执行失败*/
delete[] pInfo;
return -1;
}
// 遍历结构体,找到对应的进程
while (1)
{
// 判断是否还有下一个进程
if (pInfo->NextEntryDelta == 0)
break;
// 判断是否找到了ID
if (pInfo->ProcessId == dwProcessID)
{
// 找到该进程下的对应的线程,也就是遍历所有线程
for (DWORD i = 0; i < pInfo->ThreadCount; i++)
{
if (pInfo->Threads[i].ClientId.UniqueThread == dwThreadID)
{
// 找到线程
// 如果线程被挂起
if (pInfo->Threads[i].State == StateWait&& pInfo->Threads[i].WaitReason == Suspended)
{
nRet = 1;
break;
}
}
}
break;
}
// 迭代到下一个节点
pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);
}
delete[] pBuff;
return nRet;
}
// 设置进程状态 挂起/非挂起
int SuspendProcess(DWORD dwProcessID, BOOL fSuspend)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
THREADENTRY32 te = { sizeof(te) };
BOOL fOk = Thread32First(hSnapshot, &te);
for (; fOk; fOk = Thread32Next(hSnapshot, &te))
{
if (te.th32OwnerProcessID == dwProcessID)
{
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,FALSE, te.th32ThreadID);
if (hThread != NULL)
{
if (fSuspend)
{
if (IsThreadSuspend(dwProcessID, te.th32ThreadID) == 0)
{
SuspendThread(hThread);
}
else
{
return 0;
}
}
else
{
if (IsThreadSuspend(dwProcessID, te.th32ThreadID) == 1)
{
ResumeThread(hThread);
}
else
{
return 0;
}
}
}
CloseHandle(hThread);
}
}
}
CloseHandle(hSnapshot);
return 1;
}
int main(int argc, char *argv[])
{
// 挂起进程
SuspendProcess(20308, TRUE);
// 恢复进程
SuspendProcess(20308, FALSE);
return 0;
}
读者可自行编译并运行上述代码,通过调用 SuspendProcess 函数并以此传入需要挂起的进程PID以及一个状态,当该状态为TRUE时则代表挂起进程,而当状态值为FALSE时则代表为恢复一个进程,当一个进程被挂起后其会出现卡死的现象,当恢复后一切都会变得正常.
本文作者: 王瑞 本文链接: https://www.lyshark.com/post/5fbc3082.html 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处! 。
最后此篇关于7.6实现进程挂起与恢复的文章就讲到这里了,如果你想了解更多关于7.6实现进程挂起与恢复的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有什么方法可以恢复删除的元素吗? 这是我删除元素的代码 myFunction() { var width = window.innerWidth; var February = doc
我有一个 TokuDB 表,由于某种原因缺少 ***_status.tokudb 文件。 我还不确定文件是否由于 TokuDB 崩溃而丢失。 问题是: 有没有办法从主要文件和关键文件(我可以从 tok
我正在 Windows 7 (x86) 上运行带有 Workbench 6.3.8 的 32 位 MySQL Server 5.7.22 本地实例(必须选择 32 位版本 - 所以,较旧的版本)。 我
1、备份 <% SQL="backup database 数据库名 to disk='"&Serve
1、ASP中怎么实现SQL数据库备份、恢复! 答:asp在线备份sql server数据库: 1、备份 <% SQL="ba
我在 R 中使用 stats::filter 函数来理解 R 中的 ARIMA 模拟(如在函数 stats::arima.sim 中)和估计。我知道 stats::filter 将线性过滤器应用于向量
我已经浏览了示例应用程序的文档和代码,并发现 files/objectbox/objectbox/data.mdb 是存储所有数据的默认文件。 假设我的理解是正确的,我有几个问题找不到文档: 我想在我
为了恢复非续订订阅类型的 InAppPurchase,我已经实现了服务器来处理此问题。 但在购买过程中,iTunes 有时不会要求用户验证他们的卡详细信息, 在这种情况下,它会在后台发送应用程序并显示
我的问题是寻找cocos2d游戏期间暂停/恢复状态(包括所有需要保存的数据信息)的设计解决方案。 包括但不限于以下情况: 1).用户选择退出,然后弹出一个对话框供用户选择“直接退出”、“暂停”; 2)
在 Mercurial 中,我有一个旧的变更集,除了对单个文件的更改外,它都很好。我将如何恢复对该单个文件的更改? 即使只是能够在上一个变更集中查看文件的状态也会很好,然后我可以剪切和粘贴。 我的 M
我的一项职能遇到了困难。我想做的是计时器在页面加载后立即启动,并且只有一个带有启动/恢复的按钮。我无法在代码中找出需要更改功能的位置。有人可以帮助我吗?谢谢! HTML: , Javascr
我正在阅读Scrap your type classes 。这为类型类提供了替代方案。然而,我被Paul Chiusano的评论所困扰。其中讨论了恢复 do 表示法 语法。 坦白说,我无法理解 ret
当 OrientDB 因某人重新启动机器而非正常关闭时,OrientDB 最终会处于数据恢复失败的状态。对于如何从这种不正常的关闭中正常恢复有什么建议吗?我们正在寻找系统在断电期间能够自行恢复的方法。
我正在构建一个 Electron 应用程序,如果发生崩溃,它必须重新加载渲染进程窗口。 目前我可以从主进程重新启动应用程序 app.relaunch(); app.quit(); 但我无法检测到窗口崩
我有 3 个 Activity ,比如说 MainActivity、 Activity 2 和 Activity 3。 在 MainActivity 中,我有一个按钮(开始/停止),当我单击此按钮时,
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 11 年前。 Improve thi
Twilio 是否支持暂停和恢复内容播放。换句话说,我有相当长的文件将播放给调用者,并且我正在尝试找到一种方法来实现暂停和恢复功能。在播放某些内容的过程中,我希望用户能够按数字暂停,然后再次按数字从音
我已经提交了 A、B、C、D 和 E。我意识到在提交 B 中发生了一些非常糟糕的事情,所以我想回到 A,这次正确地进行之前搞砸了 B 的更改,然后重新应用 C 、 D 和 E 自动。 您可能想知道为什
我的一个文件被“标记为文本”,图标也发生了变化。实际上这是一个 PHP 文件。我尝试过使用 Help -> Find Action -> Mark As 尝试将其恢复为 PHP 突出显示,但它不起作用
我有一些 SSE 程序,可以将循环中的内存归零,当指针未对齐时,它会引发 SIGSEGV进入我的处理程序。我可以在此类处理程序中获取更多信息吗例行公事,现在我不知道它是在哪里完成的,我也可以吗以某种可
我是一名优秀的程序员,十分优秀!