4

Restart Manager API で奇妙な問題が発生しています: RmGetlist()。ファイル ロックのシナリオをシミュレートするために、次のサード パーティのファイル ロック ユーティリティを使用しています。

Ez ファイル ロッカー - http://www.xoslab.com/efl.html -

ファイルロッカー http://www.jensscheffler.de/filelocker

ここでの奇妙な問題は、これらのユーティリティは両方とも特定のファイルをロックしますが、最初のファイル ロック ユーティリティ (Ez ファイル ロック) では RMGetList() がアクセス拒否エラー (5) で失敗し、2 番目のファイル ロック ユーティリティでは機能することです。

ここで何が間違っている可能性がありますか?あるファイル ロック ユーティリティでは RmGetList() が失敗するのに、別のユーティリティでは機能するのはなぜですか?

以下は、使用されているコードです。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.IO;
using System.Windows.Forms;

namespace RMSession
{
    class Program
    {


        public static void GetProcessesUsingFiles(string[] filePaths)
        {
            uint sessionHandle;
            int error = NativeMethods.RmStartSession(out sessionHandle, 0, Guid.NewGuid().ToString("N"));
            if (error == 0)
            {
                try
                {
                    error = NativeMethods.RmRegisterResources(sessionHandle, (uint)filePaths.Length, filePaths, 0, null, 0, null);
                    if (error == 0)
                    {
                        RM_PROCESS_INFO[] processInfo = null;
                        uint pnProcInfoNeeded = 0, pnProcInfo = 0, lpdwRebootReasons = RmRebootReasonNone;
                        error = NativeMethods.RmGetList(sessionHandle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
                        while (error == ERROR_MORE_DATA)
                        {
                            processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                            pnProcInfo = (uint)processInfo.Length;
                            error = NativeMethods.RmGetList(sessionHandle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
                        }

                        if (error == 0 && processInfo != null)
                        {
                            for (var i = 0; i < pnProcInfo; i++)
                            {
                                RM_PROCESS_INFO procInfo = processInfo[i];
                                Process proc = null;
                                try
                                {
                                    proc = Process.GetProcessById(procInfo.Process.dwProcessId);
                                }
                                catch (ArgumentException)
                                {
                                    // Eat exceptions for processes which are no longer running.
                                }

                                if (proc != null)
                                {
                                    //yield return proc;
                                }
                            }
                        }
                    }
                }
                finally
                {
                    NativeMethods.RmEndSession(sessionHandle);
                }
            }
        }

        private const int RmRebootReasonNone = 0;
        private const int CCH_RM_MAX_APP_NAME = 255;
        private const int CCH_RM_MAX_SVC_NAME = 63;
        private const int ERROR_MORE_DATA = 234;

        [StructLayout(LayoutKind.Sequential)]
        private struct RM_UNIQUE_PROCESS
        {
            public int dwProcessId;
            public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct RM_PROCESS_INFO
        {
            public RM_UNIQUE_PROCESS Process;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
            public string strAppName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
            public string strServiceShortName;
            public RM_APP_TYPE ApplicationType;
            public uint AppStatus;
            public uint TSSessionId;
            [MarshalAs(UnmanagedType.Bool)]
            public bool bRestartable;
        }

        private enum RM_APP_TYPE
        {
            RmUnknownApp = 0,
            RmMainWindow = 1,
            RmOtherWindow = 2,
            RmService = 3,
            RmExplorer = 4,
            RmConsole = 5,
            RmCritical = 1000
        }

        [SuppressUnmanagedCodeSecurity]
        private static class NativeMethods
        {
            /// <summary>
            /// Starts a new Restart Manager session.
            /// </summary>
            /// <param name="pSessionHandle">A pointer to the handle of a Restart Manager session. The session handle can be passed in subsequent calls to the Restart Manager API.</param>
            /// <param name="dwSessionFlags">Reserved must be 0.</param>
            /// <param name="strSessionKey">A null-terminated string that contains the session key to the new session. A GUID will work nicely.</param>
            /// <returns>Error code. 0 is successful.</returns>
            [DllImport("RSTRTMGR.DLL", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]
            public static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

            /// <summary>
            /// Ends the Restart Manager session.
            /// </summary>
            /// <param name="pSessionHandle">A handle to an existing Restart Manager session.</param>
            /// <returns>Error code. 0 is successful.</returns>
            [DllImport("RSTRTMGR.DLL")]
            public static extern int RmEndSession(uint pSessionHandle);

            /// <summary>
            /// Registers resources to a Restart Manager session. 
            /// </summary>
            /// <param name="pSessionHandle">A handle to an existing Restart Manager session.</param>
            /// <param name="nFiles">The number of files being registered.</param>
            /// <param name="rgsFilenames">An array of strings of full filename paths.</param>
            /// <param name="nApplications">The number of processes being registered.</param>
            /// <param name="rgApplications">An array of RM_UNIQUE_PROCESS structures. </param>
            /// <param name="nServices">The number of services to be registered.</param>
            /// <param name="rgsServiceNames">An array of null-terminated strings of service short names.</param>
            /// <returns>Error code. 0 is successful.</returns>
            [DllImport("RSTRTMGR.DLL", CharSet = CharSet.Unicode)]
            public static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, string[] rgsFilenames, uint nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, uint nServices, string[] rgsServiceNames);

            /// <summary>
            /// Gets a list of all applications and services that are currently using resources that have been registered with the Restart Manager session.
            /// </summary>
            /// <param name="dwSessionHandle">A handle to an existing Restart Manager session.</param>
            /// <param name="pnProcInfoNeeded">A pointer to an array size necessary to receive RM_PROCESS_INFO structures</param>
            /// <param name="pnProcInfo">A pointer to the total number of RM_PROCESS_INFO structures in an array and number of structures filled.</param>
            /// <param name="rgAffectedApps">An array of RM_PROCESS_INFO structures that list the applications and services using resources that have been registered with the session.</param>
            /// <param name="lpdwRebootReasons">Pointer to location that receives a value of the RM_REBOOT_REASON enumeration that describes the reason a system restart is needed.</param>
            /// <returns>Error code. 0 is successful.</returns>
            [DllImport("RSTRTMGR.DLL")]
            public static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps, ref uint lpdwRebootReasons);
        }


        static void Main(string[] args)
        {
            Console.WriteLine("Starting...");
            string[] file1 = new string[1];
            MessageBox.Show("Debug C#");
            file1[0] = @"C:\ProcessMonitor.zip";
            //DirectoryInfo dirInfo = new DirectoryInfo(folder);

            GetProcessesUsingFiles(file1);
            Console.WriteLine("End");``
        }
    }
}
4

1 に答える 1

11

Easy File Locker は、非公式な意味でのみファイルを「ロック」しています。つまり、ファイルをアクセスから保護しますが、ファイルをロックすることによって保護するわけではありません。代わりに、ウイルス対策ソフトウェアが不正アクセスからファイルを保護する方法とほぼ同じ低レベルのテクノロジ (ファイル システム フィルタ ドライバ) を使用します。Restart Manager API は、この種のシナリオに対処することを意図しておらず、対処していません。

アプリケーションがこの種のシナリオに対処する必要がないことはほぼ確実です。これは、Easy File Locker が特定のニーズに適したツールではないことを意味します。それを捨てる。


あるファイル ロック ユーティリティでは RmGetList() が失敗するのに、別のユーティリティでは機能するのはなぜですか?

これに答えるには、RmGetList内部でどのように機能するかを理解する必要があります。あなたの場合、ファイル名を提供し、その出力はRM_PROCESS_INFO構造体の配列です。この配列を構築するために、Windows はどのプロセスがファイルを使用しているかを判断する必要があります。しかし、Windows はこれをどのように行うのでしょうか。

関数ZwQueryInformationFile( によってエクスポートされるntdll.dll) は、ファイルに関する多くの情報を返すことができます。FILE_INFORMATION_CLASS列挙のオプションの 1 つは、

FileProcessIdsUsingFileInformation

FILE_PROCESS_IDS_USING_FILE_INFORMATION構造。この値はシステム用に予約されています。この値は、Windows Vista 以降で使用できます。

そしてwdm.h(これはWindows WDKのよく知られたファイルです)で見つけます

typedef  struct _FILE_PROCESS_IDS_USING_FILE_INFORMATION {
    ULONG NumberOfProcessIdsInList;
    ULONG_PTR ProcessIdList[1];
} FILE_PROCESS_IDS_USING_FILE_INFORMATION, *PFILE_PROCESS_IDS_USING_FILE_INFORMATION;

したがって、このオプションはまさに私たちが必要としているものです!

アルゴリズムは次のようになります。

  1. アクセス権のあるファイルを開くFILE_READ_ATTRIBUTES(この情報クラスには十分です)
  2. 呼び出しZwQueryInformationFile(..,FileProcessIdsUsingFileInformation); if NumberOfProcessIdsInList!= 0 歩くProcessIdList
  3. でプロセスを開き、 ( を参照 )ProcessIdでクエリを実行し、その他のプロパティを入力します。 ProcessStartTimeGetProcessTimesRM_PROCESS_INFO

どのように機能するかがわかったので、使用している 2 つのユーティリティを見てみましょう。

2 つ目は、ソース コードを提供する非常に単純なアプリです。CreateFileで呼び出すだけdwShareMode = 0です。これにより、ファイルの排他ロックが取得され、 ordwDesiredAccess を含むでファイルを開こうとすると、 で失敗することが保証されます。ただし、 でファイルを開くことは妨げられないため、RmGetList() の呼び出しは引き続き適切に機能します。FILE_READ_DATAFILE_WRITE_DATADELETEERROR_SHARING_VIOLATIONdwDesiredAccess = FILE_READ_ATTRIBUTES

しかし、最初のツールである XOSLAB の Easy File Locker は、ミニフィルター ドライバー ( HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\xlkfs) を使用してファイルへのアクセスを制限しています。このドライバーは、ファイルを開こうとすると(STATUS_ACCESS_DENIED Win32 に変換されてERROR_ACCESS_DENIED)戻ります。このため、ステップ (1) でファイルを開こうとするとエラーが発生し、(API はこれについて何をすべきかわからないため) このエラー コードが返されます。ERROR_ACCESS_DENIEDRmGetList

それだけです。このツールは、あなたが期待していたことを単純に実行していません。

于 2016-12-27T20:51:58.723 に答える