首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 進階議題:Restart Manager 偵測檔案佔用與泛型方法 JIT 編譯共享機制

     編輯:關於.NET

本文探討兩個 .NET 進階實務議題:以程式碼偵測哪個進程正在佔用特定檔案,以及泛型方法在 JIT 編譯時的程式碼共享規則。

Restart Manager:偵測檔案佔用進程

傳統需借助 Sysinternals Handle.exe 等工具,Windows Vista+ 引入 Restart Manager API (rstrtmgr.dll),允許使用者模式程式查詢檔案佔用者。

核心 API 與 P/Invoke 宣告

[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);

[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle, UInt32 nFiles, string[] rgsFilenames, 
    UInt32 nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, UInt32 nServices, string[] rgsServiceNames);

[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, 
    [In, Out] RM_PROCESS_INFO[] rgAffectedApps, ref uint lpdwRebootReasons);

完整實作:取得佔用檔案的進程清單

public static IList<Process> GetProcessesUsingFiles(IList<string> filePaths) {
    uint sessionHandle;
    var processes = new List<Process>();

    int rv = RmStartSession(out sessionHandle, 0, Guid.NewGuid().ToString("N"));
    if (rv != 0) throw new Win32Exception();

    try {
        string[] paths = filePaths.ToArray();
        rv = RmRegisterResources(sessionHandle, (uint)paths.Length, paths, 0, null, 0, null);
        if (rv != 0) throw new Win32Exception();

        const int ERROR_MORE_DATA = 234;
        uint pnProcInfoNeeded = 0, pnProcInfo = 0, lpdwRebootReasons = 0;
        rv = RmGetList(sessionHandle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);

        if (rv == ERROR_MORE_DATA) {
            var processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
            pnProcInfo = (uint)processInfo.Length;
            rv = RmGetList(sessionHandle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
            if (rv == 0) {
                foreach (var info in processInfo) {
                    try {
                        processes.Add(Process.GetProcessById(info.Process.dwProcessId));
                    } catch (ArgumentException) { /* 進程已結束 */ }
                }
            } else throw new Win32Exception();
        } else if (rv != 0) throw new Win32Exception();
    } finally {
        RmEndSession(sessionHandle);
    }
    return processes;
}

使用範例

var processes = GetProcessesUsingFiles(new[] { @"C:\test.txt" });
foreach (var p in processes) Console.WriteLine($"{p.ProcessName} (PID: {p.Id})");

注意:僅偵測持有開啟檔案控制代碼的進程;已讀取並關閉檔案的進程不會被列出。

泛型方法 JIT 編譯共享規則

.NET 執行階段對泛型方法的原生碼生成策略:

  • 參考型別參數共享同一份程式碼:string、object、自訂類別等
  • 實值型別各自獨立編譯:int、float、struct 等各產生一份專屬程式碼

驗證方法:Visual Studio 除錯器 EIP 暫存器

  1. 在泛型方法設中斷點
  2. 開啟「暫存器」視窗,觀察 EIP(下一條指令位址)
  3. 多次觸發中斷點(不同型別參數),比對 EIP 值

實測範例

class Program {
    static void Main() {
        Test.WriteType<int>();      // 實值型別 - 獨立程式碼
        Test.WriteType<string>();   // 參考型別 - 共享程式碼
        Test.WriteType<object>();   // 參考型別 - 共享程式碼
        Test.WriteType<float>();    // 實值型別 - 獨立程式碼
        Test.WriteType<Test>();     // 參考型別 - 共享程式碼
        Test.WriteType<int>();      // 同 int - 共享程式碼
    }
}

class Test {
    public static void WriteType<T>() {
        Console.WriteLine(typeof(T).ToString()); // 設中斷點於此
    }
}

預期結果

  • string、object、Test → 相同 EIP
  • int(第一次與第六次)→ 相同 EIP
  • float → 獨特 EIP

此技巧僅適用於不支援程式碼捨棄的 JIT(如桌面版 .NET Framework),.NET Compact Framework 不適用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved