首頁  >  文章  >  後端開發  >  C#中進程的掛起與復原的程式碼實例分析(圖)

C#中進程的掛起與復原的程式碼實例分析(圖)

黄舟
黄舟原創
2017-03-27 10:59:272088瀏覽

這篇文章主要介紹了C#中進程的掛起與恢復操作方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

1. 源起:

仍然是模組化程式所引發的需求。產品經理難伺候,女產品經理更甚之~:p

純粹是戲謔,技術方案與產品經理無關,芋頭莫怪!

VCU10專案重構,要求各功能模組以獨立進程方式實現,例如:音訊視訊轉換模組,若以獨立進程方式實現,如何控制其暫停、繼續等功能?

執行緒可以Suspend、Resume,c#內建的Process沒有這類方法,咋整?

山窮水盡疑無路,柳暗花明又一村。情到濃時清轉薄,此情可待成追憶!

前篇描述了進程間資料傳遞方法,此篇亦以範例示範其間控制與資料互動方法。

 2、未公開的API函數:NtSuspendProcess、NtResumeProcess

這類函數在MSDN中找不到。

思其原因,概因它們介於Windows API和 核心API之間,威力不容小覷。怕二八耙子程式設計師濫用而引發事端,因此密藏。

其實也有NtTerminateProcess,因Process有Kill方法,因此可不用。

但再隱密的東西,只要有價值,都會被人給翻出來,好酒不怕巷子深麼!

好,基於其,設計一個行程管理類,實現模組化程式設計之進程間控制這個需求。

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&#39;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&#39;t forget to close handle you created.
    if (hProc != IntPtr.Zero)
     CloseHandle(hProc);
   }
  }
 }

4.進程控制

我權且主進程為宿主,它透過Process類別呼叫子進程,得其ID,以此為用。其呼叫代碼為:

 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、效果圖:

為範例,做了兩個圖,其一為顯示子進程,其二為隱藏子進程。

實際專案呼叫獨立進程模組,是以隱藏方式呼叫的,以宿主展示其處理進度,如此圖:

後記:

擴充思路,一些優秀的開源工具,如youtube_dl、ffmpeg等,都以獨立進程方式存在,且可透過CMD管理通訊。

以此流程控制原理,可以基於這些開源工具,做出相當不錯的GUI工具出來。畢竟相對於強大的命令列,人們還是以簡單操作為方便。

以上是C#中進程的掛起與復原的程式碼實例分析(圖)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn