UltimateZoneBuilder/Source/Core/General/FileLockChecker.cs

212 lines
6.8 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace CodeImp.DoomBuilder
{
internal static class FileLockChecker
{
internal class FileLockCheckResult //mxd
{
public string Error;
public List<Process> Processes = new List<Process>();
}
[StructLayout(LayoutKind.Sequential)]
private struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
private const int RmRebootReasonNone = 0;
private const int CCH_RM_MAX_APP_NAME = 255;
private const int CCH_RM_MAX_SVC_NAME = 63;
private enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[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;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
private static extern int RmRegisterResources(uint pSessionHandle,
UInt32 nFiles,
string[] rgsFilenames,
UInt32 nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
UInt32 nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
private static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
private static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
private static extern int RmGetList(uint dwSessionHandle,
out uint pnProcInfoNeeded,
ref uint pnProcInfo,
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
///
/// </remarks>
static public FileLockCheckResult CheckFile(string path)
{
//mxd. Do it the clunky way? (WinXP)
if(Environment.OSVersion.Version.Major < 6)
{
bool locked = false;
try
{
using(File.Open(path, FileMode.Open)) { }
}
catch(IOException e)
{
int errorcode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
locked = (errorcode == 32 || errorcode == 33);
}
return new FileLockCheckResult { Error = (locked ? "Unable to save the map. Map file is locked by another process." : string.Empty) };
}
//mxd. Needs Vista or newer...
uint handle;
string key = Guid.NewGuid().ToString();
FileLockCheckResult result = new FileLockCheckResult(); //mxd
string errorstart = "Unable to save the map: target file is locked by another process."
+ Environment.NewLine + "Also, unable to get the name of the offending process:"
+ Environment.NewLine + Environment.NewLine;
int res = RmStartSession(out handle, 0, key);
if(res != 0)
{
Added, Texture Browser: added "Show textures in subdirectories" checkbox (enabled by default). When enabled, textures from current PK3/PK7/Directory resource directory and it's subdirectories will be shown. Otherwise, only textures from current directory will be shown. Removed, Texture Browser: removed "Show image sizes" checkbox. "Show texture and flat sizes in browsers" preferences setting is now used instead. Fixed, Things mode: event line between pre-last and the last PatrolPoint was not drawn. Fixed, Things mode: highlight range for sizeless things (things with "fixedsize" game configuration property) was calculated incorrectly. Fixed: fixed a crash when opening Script Editor after using "Open map in current wad" command to switch to UDMF map with SCRIPTS lump when current script configuration was not saved in the wad's .dbs file. Fixed: map closing events were not triggered when using "Open map in current wad" command, which could potentially result in plugin crashes/incorrect behavior. Fixed: Sector Drawing overrides panel could trigger an exception when closing the map during resource loading. Internal: added "Debug + Profiler" solution configuration, added 2 profiling methods to DebugConsole. Internal: rewrote MainForm.DisplayStatus() / StatusInfo to handle selection info in a more structured way. Fixed, internal: some destructors could potentially be executed more than once potentially leading to exceptions. Other destructors were not called at all. Updated ZDoom_DECORATE.cfg.
2015-09-16 12:10:43 +00:00
RmEndSession(handle); //mxd
result.Error = errorstart + "Error " + res + ". Could not begin restart session. Unable to determine file locker."; //mxd
return result;
}
try
{
const int ERROR_MORE_DATA = 234;
uint pnProcInfoNeeded,
pnProcInfo = 0,
lpdwRebootReasons = RmRebootReasonNone;
string[] resources = new[] { path }; // Just checking on one resource.
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
if(res != 0)
{
result.Error = errorstart + "Error " + res + ". Could not register resource."; //mxd
return result;
}
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
if(res == ERROR_MORE_DATA)
{
// Create an array to store the process results
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = pnProcInfoNeeded;
// Get the list
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
if(res == 0)
{
result.Processes = new List<Process>((int)pnProcInfo);
// Enumerate all of the results and add them to the list to be returned
for(int i = 0; i < pnProcInfo; i++)
{
try
{
Process process = Process.GetProcessById(processInfo[i].Process.dwProcessId);
result.Processes.Add(process);
}
// catch the error -- in case the process is no longer running
catch(ArgumentException) {}
}
//mxd
if(result.Processes.Count > 0)
{
result.Error = "Unable to save the map: target file is locked by the following process"
+ (result.Processes.Count > 1 ? "es" : "") + ":"
+ Environment.NewLine + Environment.NewLine;
foreach(Process process in result.Processes)
{
string processpath = string.Empty;
try
{
// All manner of exceptions are possible here...
processpath = process.MainModule.FileName;
}catch {}
result.Error += process.ProcessName
+ " (" + (!string.IsNullOrEmpty(processpath) ? "\"" + processpath + "\"" : "")
+ ", started at " + process.StartTime + ")"
+ Environment.NewLine;
}
}
}
else
{
result.Error = "Error " + res + ". Could not list processes locking the resource."; //mxd
return result;
}
}
else if(res != 0)
{
result.Error = "Error " + res + ". Could not list processes locking resource. Failed to get result size."; //mxd
return result;
}
}
finally
{
RmEndSession(handle);
}
return result;
}
}
}