Changed: when the editor is unable to save the map, it will now display the list of processes, which lock the map file.

Fixed, Update Checker: the existence of Updater.exe and Updater.ini was not checked when using Help -> Check for updates command.
This commit is contained in:
MaxED 2015-09-06 22:15:21 +00:00
parent 008846539b
commit 6dc100321a
7 changed files with 223 additions and 106 deletions

View file

@ -772,6 +772,7 @@
<Compile Include="General\CRC.cs" />
<Compile Include="General\ErrorItem.cs" />
<Compile Include="General\ErrorLogger.cs" />
<Compile Include="General\FileLockChecker.cs" />
<Compile Include="General\MurmurHash2.cs" />
<Compile Include="General\SavePurpose.cs" />
<Compile Include="General\UpdateChecker.cs" />

View file

@ -0,0 +1,174 @@
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<string> ProcessInfos = new List<string>();
}
[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)
{
uint handle;
string key = Guid.NewGuid().ToString();
FileLockCheckResult result = new FileLockCheckResult(); //mxd
int res = RmStartSession(out handle, 0, key);
if(res != 0)
{
result.Error = "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 = "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)
{
List<Process> 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
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
// catch the error -- in case the process is no longer running
catch(ArgumentException) {}
}
//mxd
foreach(Process process in processes)
{
if(General.ThisAssembly.Location == process.MainModule.FileName) continue; //don't count ourselves
result.ProcessInfos.Add(Path.GetFileName(process.MainModule.FileName)
+ " ('" + process.MainModule.FileName
+ "', started at " + process.StartTime + ")");
}
}
else
{
result.Error = "ERROR " + res + ". Could not list processes locking resource."; //mxd
return result;
}
}
else if(res != 0)
{
result.Error = "ERROR " + res + ". Could not list processes locking resource. Failed to get size of result."; //mxd
return result;
}
}
finally
{
RmEndSession(handle);
}
return result;
}
}
}

View file

@ -729,21 +729,7 @@ namespace CodeImp.DoomBuilder
}
//mxd. Check for updates?
if(General.Settings.CheckForUpdates)
{
if(!File.Exists(Path.Combine(apppath, "Updater.exe")))
{
General.ErrorLogger.Add(ErrorType.Warning, "Update check failed: Updater.exe does not exist!");
}
else if(!File.Exists(Path.Combine(apppath, "Updater.ini")))
{
General.ErrorLogger.Add(ErrorType.Warning, "Update check failed: Updater.ini does not exist!");
}
else
{
UpdateChecker.PerformCheck(false);
}
}
if(General.Settings.CheckForUpdates) UpdateChecker.PerformCheck(false);
// Run application from the main window
Application.Run(mainwindow);
@ -1076,13 +1062,6 @@ namespace CodeImp.DoomBuilder
[BeginAction("newmap")]
internal static void NewMap()
{
//mxd
if(map != null && map.Launcher.GameEngineRunning)
{
ShowWarningMessage("Cannot create a map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
return;
}
MapOptions newoptions = new MapOptions();
// Cancel volatile mode, if any
@ -1160,13 +1139,6 @@ namespace CodeImp.DoomBuilder
internal static void ActionCloseMap() { CloseMap(); }
internal static bool CloseMap()
{
//mxd
if(map != null && map.Launcher.GameEngineRunning)
{
ShowWarningMessage("Cannot close the map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
return false;
}
// Cancel volatile mode, if any
editing.DisengageVolatileMode();
@ -1220,12 +1192,6 @@ namespace CodeImp.DoomBuilder
[BeginAction("openmap")]
internal static void OpenMap()
{
if(map != null && map.Launcher.GameEngineRunning) //mxd
{
ShowWarningMessage("Cannot open a map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
return;
}
// Cancel volatile mode, if any
editing.DisengageVolatileMode();
@ -1264,13 +1230,6 @@ namespace CodeImp.DoomBuilder
return;
}
//mxd
if(map.Launcher.GameEngineRunning)
{
ShowWarningMessage("Cannot change the map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
return;
}
// Cancel volatile mode, if any
Editing.DisengageVolatileMode();
@ -1457,14 +1416,6 @@ namespace CodeImp.DoomBuilder
internal static bool SaveMap()
{
if(map == null) return false;
//mxd
if (map.Launcher.GameEngineRunning)
{
ShowWarningMessage("Cannot save the map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
return false;
}
bool result = false;
// Cancel volatile mode, if any
@ -1535,14 +1486,6 @@ namespace CodeImp.DoomBuilder
internal static bool SaveMapAs()
{
if(map == null) return false;
//mxd
if(map.Launcher.GameEngineRunning)
{
ShowWarningMessage("Cannot save the map while a game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
return false;
}
bool result = false;
// Cancel volatile mode, if any
@ -1615,14 +1558,6 @@ namespace CodeImp.DoomBuilder
internal static bool SaveMapInto()
{
if(map == null) return false;
//mxd
if(map.Launcher.GameEngineRunning)
{
ShowWarningMessage("Cannot save the map while game engine is running" + Environment.NewLine + "Please close '" + map.ConfigSettings.TestProgram + "' first.", MessageBoxButtons.OK);
return false;
}
bool result = false;
// Cancel volatile mode, if any

View file

@ -49,7 +49,6 @@ namespace CodeImp.DoomBuilder
#region ================== Properties
public string TempWAD { get { return tempwad; } }
public bool GameEngineRunning { get { return process != null; } } //mxd
#endregion
@ -251,16 +250,9 @@ namespace CodeImp.DoomBuilder
General.Editing.Mode.OnMapTestEnd(true);
}
// This saves the map to a temporary file and launches a test wit hthe given skill
// This saves the map to a temporary file and launches a test with the given skill
public void TestAtSkill(int skill)
{
//mxd
if (process != null)
{
General.ShowWarningMessage("Game engine is already running." + Environment.NewLine + " Please close '" + General.Map.ConfigSettings.TestProgram + "' before testing again", MessageBoxButtons.OK);
return;
}
Cursor oldcursor = Cursor.Current;
// Check if configuration is OK
@ -362,21 +354,6 @@ namespace CodeImp.DoomBuilder
if(General.Editing.Mode is ClassicMode) General.MainWindow.RedrawDisplay();
}
//mxd
public void StopGameEngine()
{
//mxd. Terminate process?
if(process != null)
{
process.CloseMainWindow();
process.Close();
process = null;
// Remove temporary file
try { if(File.Exists(tempwad)) File.Delete(tempwad); } catch(Exception) { }
}
}
//mxd
private void ProcessOnExited(object sender, EventArgs eventArgs)
{

View file

@ -577,13 +577,29 @@ namespace CodeImp.DoomBuilder
// Initializes for an existing map
internal bool SaveMap(string newfilepathname, SavePurpose purpose)
{
MapSet outputset;
string nodebuildername, settingsfile;
StatusInfo oldstatus;
//mxd. Check if the target file is locked
FileLockChecker.FileLockCheckResult checkresult = FileLockChecker.CheckFile(newfilepathname);
if(!string.IsNullOrEmpty(checkresult.Error))
{
General.ShowErrorMessage("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 + checkresult.Error
, MessageBoxButtons.OK);
return false;
}
if(checkresult.ProcessInfos.Count > 0)
{
General.ShowErrorMessage("Unable to save the map: target file is locked by the following process" + (checkresult.ProcessInfos.Count > 1 ? "es" : "") + ":"
+ Environment.NewLine + Environment.NewLine
+ string.Join(Environment.NewLine + Environment.NewLine, checkresult.ProcessInfos.ToArray())
, MessageBoxButtons.OK);
return false;
}
string settingsfile;
WAD targetwad;
int index;
bool includenodes;
string origmapname;
General.WriteLogLine("Saving map to file: " + newfilepathname);
@ -614,7 +630,7 @@ namespace CodeImp.DoomBuilder
if (changed)
{
// Make a copy of the map data
outputset = map.Clone();
MapSet outputset = map.Clone();
// Remove all flags from all 3D Start things
foreach (Thing t in outputset.Things)
@ -629,6 +645,7 @@ namespace CodeImp.DoomBuilder
}
// Do we need sidedefs compression?
StatusInfo oldstatus;
if (map.Sidedefs.Count > io.MaxSidedefs)
{
// Compress sidedefs
@ -684,15 +701,12 @@ namespace CodeImp.DoomBuilder
outputset.Dispose();
// Get the corresponding nodebuilder
nodebuildername = (purpose == SavePurpose.Testing) ? configinfo.NodebuilderTest : configinfo.NodebuilderSave;
string nodebuildername = (purpose == SavePurpose.Testing) ? configinfo.NodebuilderTest : configinfo.NodebuilderSave;
// Build the nodes
oldstatus = General.MainWindow.Status;
General.MainWindow.DisplayStatus(StatusType.Busy, "Building map nodes...");
if (!string.IsNullOrEmpty(nodebuildername))
includenodes = BuildNodes(nodebuildername, true);
else
includenodes = false;
includenodes = (!string.IsNullOrEmpty(nodebuildername) && BuildNodes(nodebuildername, true));
General.MainWindow.DisplayStatus(oldstatus);
}
else
@ -705,7 +719,7 @@ namespace CodeImp.DoomBuilder
data.Suspend();
// Determine original map name
origmapname = (options.PreviousName != "" && purpose != SavePurpose.IntoFile) ? options.PreviousName : options.CurrentName;
string origmapname = (options.PreviousName != "" && purpose != SavePurpose.IntoFile) ? options.PreviousName : options.CurrentName;
string origwadfile = string.Empty; //mxd
try

View file

@ -16,6 +16,7 @@ namespace CodeImp.DoomBuilder
internal static void PerformCheck(bool verbosemode)
{
// Update check already runing?
if(worker != null && worker.IsBusy)
{
if(verbosemode)
@ -30,6 +31,7 @@ namespace CodeImp.DoomBuilder
return;
}
// Start checking
verbose = verbosemode;
worker = new BackgroundWorker();
worker.DoWork += DoWork;
@ -40,7 +42,23 @@ namespace CodeImp.DoomBuilder
private static void DoWork(object sender, DoWorkEventArgs e)
{
string url = GetDownloadUrl(Path.Combine(General.AppPath, "Updater.ini"));
string updaterpath = Path.Combine(General.AppPath, "Updater.exe");
if(!File.Exists(updaterpath))
{
errordesc = "Update check failed: '" + updaterpath + "' does not exist!";
e.Cancel = true;
return;
}
string inipath = Path.Combine(General.AppPath, "Updater.ini");
if(!File.Exists(inipath))
{
errordesc = "Update check failed: '" + inipath + "' does not exist!";
e.Cancel = true;
return;
}
string url = GetDownloadUrl(inipath);
if(string.IsNullOrEmpty(url))
{
errordesc = "Update check failed: failed to get update url from Updater.ini!";
@ -50,6 +68,7 @@ namespace CodeImp.DoomBuilder
// Get local revision number
int localrev = General.ThisAssembly.GetName().Version.Revision;
int actuallocalrev = localrev;
if(!verbose) localrev = Math.Max(localrev, General.Settings.IgnoredRemoteRevision);
// Get remote revision number
@ -80,7 +99,7 @@ namespace CodeImp.DoomBuilder
if(remoterev > localrev)
{
// Get changelog info
string changelog = GetChangelog(url, localrev);
string changelog = GetChangelog(url, actuallocalrev);
if(string.IsNullOrEmpty(changelog))
{

View file

@ -650,10 +650,7 @@ namespace CodeImp.DoomBuilder.Windows
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if(e.CloseReason == CloseReason.ApplicationExitCall) return;
if(General.Map != null && General.Map.Launcher.GameEngineRunning)
General.Map.Launcher.StopGameEngine(); //mxd
// Close the map
if(General.CloseMap())