C#中進程的掛起與恢復
1. 源起:
仍然是模塊化編程所引發(fā)的需求。產(chǎn)品經(jīng)理難伺候,女產(chǎn)品經(jīng)理更甚之~:p
純屬戲謔,技術方案與產(chǎn)品經(jīng)理無關,芋頭莫怪!
VCU10項目重構,要求各功能模塊以獨立進程方式實現(xiàn),比如:音視頻轉(zhuǎn)換模塊,若以獨立進程方式實現(xiàn),如何控制其暫停、繼續(xù)等功能呢?
線程可以Suspend、Resume,c#內(nèi)置的Process沒有此類方法,咋整?
山窮水盡疑無路,柳暗花明又一村。情到濃時清轉(zhuǎn)薄,此情可待成追憶!
前篇描述了進程間數(shù)據(jù)傳遞方法,此篇亦以示例演示其間控制與數(shù)據(jù)交互方法。
2、未公開的API函數(shù):NtSuspendProcess、NtResumeProcess
此類函數(shù)在MSDN中找不到。
思其原因,概因它們介于Windows API和 內(nèi)核API之間,威力不容小覷。怕二八耙子程序員濫用而引發(fā)事端,因此密藏。
其實還有個NtTerminateProcess,因Process有Kill方法,因此可不用。
但再隱秘的東西,只要有價值,都會被人給翻出來,好酒不怕巷子深么!
好,基于其,設計一個進程管理類,實現(xiàn)模塊化編程之進程間控制這個需求。
3、ProcessMgr
直上代碼吧,封裝一個進程管理單元:
public static class ProcessMgr
{
/// <summary>
/// The process-specific access rights.
/// </summary>
[Flags]
public enum ProcessAccess : uint
{
/// <summary>
/// Required to terminate a process using TerminateProcess.
/// </summary>
Terminate = 0x1,
/// <summary>
/// Required to create a thread.
/// </summary>
CreateThread = 0x2,
/// <summary>
/// Undocumented.
/// </summary>
SetSessionId = 0x4,
/// <summary>
/// Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
/// </summary>
VmOperation = 0x8,
/// <summary>
/// Required to read memory in a process using ReadProcessMemory.
/// </summary>
VmRead = 0x10,
/// <summary>
/// Required to write to memory in a process using WriteProcessMemory.
/// </summary>
VmWrite = 0x20,
/// <summary>
/// Required to duplicate a handle using DuplicateHandle.
/// </summary>
DupHandle = 0x40,
/// <summary>
/// Required to create a process.
/// </summary>
CreateProcess = 0x80,
/// <summary>
/// Required to set memory limits using SetProcessWorkingSetSize.
/// </summary>
SetQuota = 0x100,
/// <summary>
/// Required to set certain information about a process, such as its priority class (see SetPriorityClass).
/// </summary>
SetInformation = 0x200,
/// <summary>
/// Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
/// </summary>
QueryInformation = 0x400,
/// <summary>
/// Undocumented.
/// </summary>
SetPort = 0x800,
/// <summary>
/// Required to suspend or resume a process.
/// </summary>
SuspendResume = 0x800,
/// <summary>
/// Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION.
/// </summary>
QueryLimitedInformation = 0x1000,
/// <summary>
/// Required to wait for the process to terminate using the wait functions.
/// </summary>
Synchronize = 0x100000
}
[DllImport("ntdll.dll")]
private static extern uint NtResumeProcess([In] IntPtr processHandle);
[DllImport("ntdll.dll")]
private static extern uint NtSuspendProcess([In] IntPtr processHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(
ProcessAccess desiredAccess,
bool inheritHandle,
int processId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle([In] IntPtr handle);
public static void SuspendProcess(int processId)
{
IntPtr hProc = IntPtr.Zero;
try
{
// Gets the handle to the Process
hProc = OpenProcess(ProcessAccess.SuspendResume, false, processId);
if (hProc != IntPtr.Zero)
NtSuspendProcess(hProc);
}
finally
{
// Don't forget to close handle you created.
if (hProc != IntPtr.Zero)
CloseHandle(hProc);
}
}
public static void ResumeProcess(int processId)
{
IntPtr hProc = IntPtr.Zero;
try
{
// Gets the handle to the Process
hProc = OpenProcess(ProcessAccess.SuspendResume, false, processId);
if (hProc != IntPtr.Zero)
NtResumeProcess(hProc);
}
finally
{
// Don't forget to close handle you created.
if (hProc != IntPtr.Zero)
CloseHandle(hProc);
}
}
}
4、進程控制
我權且主進程為宿主,它通過Process類調(diào)用子進程,得其ID,以此為用。其調(diào)用代碼為:
private void RunTestProcess(bool hidden = false)
{
string appPath = Path.GetDirectoryName(Application.ExecutablePath);
string testAppPath = Path.Combine(appPath, "TestApp.exe");
var pi = new ProcessStartInfo();
pi.FileName = testAppPath;
pi.Arguments = this.Handle.ToString();
pi.WindowStyle = hidden ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal;
this.childProcess = Process.Start(pi);
txtInfo.Text = string.Format("子進程ID:{0}\r\n子進程名:{1}", childProcess.Id, childProcess.ProcessName);
...
}
控制代碼為:
private void btnWork_Click(object sender, EventArgs e)
{
if (this.childProcess == null || this.childProcess.HasExited)
return;
if ((int)btnWork.Tag == 0)
{
btnWork.Tag = 1;
btnWork.Text = "恢復";
ProcessMgr.SuspendProcess(this.childProcess.Id);
}
else
{
btnWork.Tag = 0;
btnWork.Text = "掛起";
ProcessMgr.ResumeProcess(this.childProcess.Id);
}
}
子進程以一定時器模擬其工作,向主進程拋進度消息:
private void timer_Tick(object sender, EventArgs e)
{
if (progressBar.Value < progressBar.Maximum)
progressBar.Value += 1;
else
progressBar.Value = 0;
if (this.hostHandle != IntPtr.Zero)
SendMessage(this.hostHandle, WM_PROGRESS, 0, progressBar.Value);
}
代碼量就這么的少,簡單吧……
5、效果圖:
為示例,做了兩個圖,其一為顯示子進程,其二為隱藏子進程。

實際項目調(diào)用獨立進程模塊,是以隱藏方式調(diào)用的,以宿主展示其處理進度,如此圖:

后記:
擴展思路,一些優(yōu)秀的開源工具,如youtube_dl、ffmpeg等,都以獨立進程方式存在,且可通過CMD管理通信。
以此進程控制原理,可以基于這些開源工具,做出相當不錯的GUI工具出來。畢竟相對于強大的命令行,人們還是以簡單操作為方便。
相關文章
Unity3D UI Text得分數(shù)字增加的實例代碼
這篇文章主要介紹了Unity3D UI Text得分數(shù)字增加方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04
C#?連接本地數(shù)據(jù)庫的實現(xiàn)示例
本文主要介紹了C#?連接本地數(shù)據(jù)庫的實現(xiàn)示例,文中根據(jù)實例編碼詳細介紹的十分詳盡,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
C#對二進制數(shù)據(jù)進行base64編碼的方法
這篇文章主要介紹了C#對二進制數(shù)據(jù)進行base64編碼的方法,涉及C#中Convert.ToBase64String用法技巧,需要的朋友可以參考下2015-04-04

