mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-02-17 01:22:18 +00:00
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:
parent
008846539b
commit
6dc100321a
7 changed files with 223 additions and 106 deletions
|
@ -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" />
|
||||
|
|
174
Source/Core/General/FileLockChecker.cs
Normal file
174
Source/Core/General/FileLockChecker.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in a new issue