diff --git a/irc/TechBot/Resources/hresult.xml b/irc/TechBot/Resources/hresult.xml index 743d04012e2..68e5bdc55e2 100644 --- a/irc/TechBot/Resources/hresult.xml +++ b/irc/TechBot/Resources/hresult.xml @@ -1,5 +1,7 @@  + + diff --git a/irc/TechBot/Resources/ntstatus.xml b/irc/TechBot/Resources/ntstatus.xml index ce44f8d5ca7..2e99a3bd13d 100644 --- a/irc/TechBot/Resources/ntstatus.xml +++ b/irc/TechBot/Resources/ntstatus.xml @@ -2,8 +2,69 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/irc/TechBot/TechBot.Console/App.config b/irc/TechBot/TechBot.Console/App.config index 397d4b0773c..a7d7eeb7a5f 100644 --- a/irc/TechBot/TechBot.Console/App.config +++ b/irc/TechBot/TechBot.Console/App.config @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/irc/TechBot/TechBot.Library/ErrorCommand.cs b/irc/TechBot/TechBot.Library/ErrorCommand.cs index 6ee2d68f865..35b7789b71e 100644 --- a/irc/TechBot/TechBot.Library/ErrorCommand.cs +++ b/irc/TechBot/TechBot.Library/ErrorCommand.cs @@ -1,5 +1,6 @@ using System; using System.Xml; +using System.Collections; namespace TechBot.Library { @@ -28,58 +29,170 @@ namespace TechBot.Library new string[] { "error" }); } + private static int GetSeverity(long error) + { + return (int)((error >> 30) & 0x3); + } + + private static bool IsCustomer(long error) + { + return (error & 0x20000000) != 0; + } + + private static bool IsReserved(long error) + { + return (error & 0x10000000) != 0; + } + + private static int GetFacility(long error) + { + return (int)((error >> 16) & 0xFFF); + } + + private static short GetCode(long error) + { + return (short)((error >> 0) & 0xFFFF); + } + + private static string FormatSeverity(long error) + { + int severity = GetSeverity(error); + switch (severity) + { + case 0: return "SUCCESS"; + case 1: return "INFORMATIONAL"; + case 2: return "WARNING"; + case 3: return "ERROR"; + } + return null; + } + + private static string FormatFacility(long error) + { + int facility = GetFacility(error); + return facility.ToString(); + } + + private static string FormatCode(long error) + { + int code = GetCode(error); + return code.ToString(); + } + public void Handle(MessageContext context, string commandName, string parameters) { - string errorText = parameters; - if (errorText.Equals(String.Empty)) + string originalErrorText = parameters.Trim(); + if (originalErrorText.Equals(String.Empty)) { serviceOutput.WriteLine(context, "Please provide an Error Code."); return; } + + string errorText = originalErrorText; + retry: NumberParser np = new NumberParser(); long error = np.Parse(errorText); if (np.Error) { serviceOutput.WriteLine(context, String.Format("{0} is not a valid Error Code.", - errorText)); + originalErrorText)); return; } + + ArrayList descriptions = new ArrayList(); - string description = null; - if (winerror.GetWinerrorDescription(error) != null) + // Error is out of bounds + if ((ulong)error > uint.MaxValue) { - description = winerror.GetWinerrorDescription(error); - serviceOutput.WriteLine(context, - String.Format("{0} is {1}.", - error, - description)); + // Do nothing } - if (ntStatus.GetNtstatusDescription(error) != null) + // Error is outside of the range [0, 65535]: it cannot be a plain Win32 error code + else if ((ulong)error > ushort.MaxValue) { - description = ntStatus.GetNtstatusDescription(error); - serviceOutput.WriteLine(context, - String.Format("{0} is {1}.", - errorText, - description)); + // Customer bit is set: custom error code + if (IsCustomer(error)) + { + string description = String.Format("[custom, severity {0}, facility {1}, code {2}]", + FormatSeverity(error), + FormatFacility(error), + FormatCode(error)); + descriptions.Add(description); + } + // Reserved bit is set: HRESULT_FROM_NT(ntstatus) + else if (IsReserved(error)) + { + int status = (int)(error & 0xCFFFFFFF); + string description = ntStatus.GetNtstatusDescription(status); + + if (description == null) + description = status.ToString("X"); + + description = String.Format("HRESULT_FROM_NT({0})", description); + descriptions.Add(description); + } + // Win32 facility: HRESULT_FROM_WIN32(winerror) + else if (GetFacility(error) == 7) + { + // Must be an error code + if (GetSeverity(error) == 2) + { + short err = GetCode(error); + string description = winerror.GetWinerrorDescription(err); + + if (description == null) + description = err.ToString("D"); + + description = String.Format("HRESULT_FROM_WIN32({0})", description); + descriptions.Add(description); + } + } } - if (hresult.GetHresultDescription(error) != null) - { - description = hresult.GetHresultDescription(error); - serviceOutput.WriteLine(context, - String.Format("{0} is {1}.", - errorText, - description)); - } - if(description == null) + + string winerrorDescription = winerror.GetWinerrorDescription(error); + string ntstatusDescription = ntStatus.GetNtstatusDescription(error); + string hresultDescription = hresult.GetHresultDescription(error); + + if (winerrorDescription != null) + descriptions.Add(winerrorDescription); + if (ntstatusDescription != null) + descriptions.Add(ntstatusDescription); + if (hresultDescription != null) + descriptions.Add(hresultDescription); + + if (descriptions.Count == 0) { + // Last chance heuristics: attempt to parse a 8-digit decimal as hexadecimal + if (errorText.Length == 8) + { + errorText = "0x" + errorText; + goto retry; + } + serviceOutput.WriteLine(context, String.Format("I don't know about Error Code {0}.", - errorText)); + originalErrorText)); + } + else if (descriptions.Count == 1) + { + string description = (string)descriptions[0]; + serviceOutput.WriteLine(context, + String.Format("{0} is {1}.", + originalErrorText, + description)); + } + else + { + serviceOutput.WriteLine(context, + String.Format("{0} could be:", + originalErrorText)); + + foreach(string description in descriptions) + serviceOutput.WriteLine(context, String.Format("\t{0}", description)); } } diff --git a/irc/TechBot/TechBot.Library/HresultCommand.cs b/irc/TechBot/TechBot.Library/HresultCommand.cs index c17b577c912..32b76672b30 100644 --- a/irc/TechBot/TechBot.Library/HresultCommand.cs +++ b/irc/TechBot/TechBot.Library/HresultCommand.cs @@ -6,14 +6,12 @@ namespace TechBot.Library public class HresultCommand : BaseCommand, ICommand { private IServiceOutput serviceOutput; - private string hresultXml; private XmlDocument hresultXmlDocument; public HresultCommand(IServiceOutput serviceOutput, string hresultXml) { this.serviceOutput = serviceOutput; - this.hresultXml = hresultXml; hresultXmlDocument = new XmlDocument(); hresultXmlDocument.Load(hresultXml); } diff --git a/irc/TechBot/TechBot.Library/NumberParser.cs b/irc/TechBot/TechBot.Library/NumberParser.cs index 15521ede73b..232ed0be1f2 100644 --- a/irc/TechBot/TechBot.Library/NumberParser.cs +++ b/irc/TechBot/TechBot.Library/NumberParser.cs @@ -9,7 +9,7 @@ namespace TechBot.Library private const string SpecialHexCharacters = "ABCDEF"; - private bool IsSpecialHexCharacter(char ch) + private static bool IsSpecialHexCharacter(char ch) { foreach (char specialChar in SpecialHexCharacters) { @@ -19,7 +19,7 @@ namespace TechBot.Library return false; } - private bool HasSpecialHexCharacters(string s) + private static bool HasSpecialHexCharacters(string s) { foreach (char ch in s) { @@ -35,7 +35,7 @@ namespace TechBot.Library { Error = false; bool useHex = false; - if (s.StartsWith("0x")) + if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) { s = s.Substring(2); useHex = true; diff --git a/irc/TechBot/TechBot.Library/TechBotService.cs b/irc/TechBot/TechBot.Library/TechBotService.cs index 1efb858f5d3..f727c33fc00 100644 --- a/irc/TechBot/TechBot.Library/TechBotService.cs +++ b/irc/TechBot/TechBot.Library/TechBotService.cs @@ -49,9 +49,9 @@ namespace TechBot.Library { commands.Add(new HelpCommand(serviceOutput, commands)); - commands.Add(new ApiCommand(serviceOutput, + /*commands.Add(new ApiCommand(serviceOutput, chmPath, - mainChm)); + mainChm));*/ commands.Add(new NtStatusCommand(serviceOutput, ntstatusXml)); commands.Add(new WinerrorCommand(serviceOutput,