diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 74951f683..270596ecf 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -1,3 +1,20 @@ +1.75.7005 (2017-04-27) + +Fixes: +------ +#410: Update PuTTYNG to 0.68 +#434: Fix complier warnings CA1049 & CA2111 +#442: Fixed issue loading PuTTY sessions that have spaces in the name +#502: Problems with ParentID for Duplicated Containers/Connections with SQL Connection Storage +#514: Expanded property not saved/loaded properly from SQL +#518: Exception when Importing File + + +General Changes: +---------------- +Minor code cleanup/optimizations/null checks + + 1.75.7003 (2017-03-24): Fixes: diff --git a/CREDITS.TXT b/CREDITS.TXT index 2b5922afd..c902036a2 100644 --- a/CREDITS.TXT +++ b/CREDITS.TXT @@ -99,8 +99,8 @@ Copyright Freely redistributable with attribution http://www.dotnetmagic.com/magic_download.html -PuTTY 0.67 -Copyright © 1997-2016 Simon Tatham +PuTTY 0.68 +Copyright © 1997-2017 Simon Tatham MIT License http://www.chiark.greenend.org.uk/~sgtatham/putty/ diff --git a/InstallerProjects/Installer/Includes/Config.wxi b/InstallerProjects/Installer/Includes/Config.wxi index e54e30ecd..4ce5e61ba 100644 --- a/InstallerProjects/Installer/Includes/Config.wxi +++ b/InstallerProjects/Installer/Includes/Config.wxi @@ -17,7 +17,7 @@ - + diff --git a/InstallerProjects/Installer/Localizations/en-US.wxl b/InstallerProjects/Installer/Localizations/en-US.wxl index 6812f546e..0677bd9df 100644 --- a/InstallerProjects/Installer/Localizations/en-US.wxl +++ b/InstallerProjects/Installer/Localizations/en-US.wxl @@ -6,7 +6,7 @@ You need to be an administrator to install this product. mRemoteNG requires Microsoft .NET Framework [REQUIREDDOTNETFRAMEWORKVERSION] or higher. mRemoteNG requires Windows 7 SP1 or higher to run. Please update your operating system and try again. - mRemoteNG requires RDP 8.0 or higher to run. Windows 7 users will need to install KB2592687 + mRemoteNG requires RDP 8.0 or higher to run. Windows 7 users will need to install either KB2592687 (RDP 8.0) or KB2830477 (RDP 8.1) mRemoteNG requires KB2574819 in order to create RDP connections. Windows 7 users will need to install this KB. For mRemoteNG to run on Windows 7, it requires Service Pack 1 to be installed. Please install Service Pack 1 and try again. diff --git a/InstallerProjects/Installer/mRemoteNGV1.wxs b/InstallerProjects/Installer/mRemoteNGV1.wxs index 8bacd0b1e..b1d393574 100644 --- a/InstallerProjects/Installer/mRemoteNGV1.wxs +++ b/InstallerProjects/Installer/mRemoteNGV1.wxs @@ -67,7 +67,7 @@ = 602 OR VersionNT64 >= 602) OR ((VersionNT = 601 OR VersionNT64 = 601) AND (RDP_DTLS_UPDATE_INSTALLED = 1))]]> - + = 602 OR VersionNT64 >= 602) OR ((VersionNT = 601 OR VersionNT64 = 601) AND (MINIMUM_RDP_VERSION_INSTALLED = 1))]]> diff --git a/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs new file mode 100644 index 000000000..b9fd42522 --- /dev/null +++ b/mRemoteNGTests/Config/Serializers/DataTableDeserializerTests.cs @@ -0,0 +1,55 @@ +using System.Data; +using System.Linq; +using mRemoteNG.Config.Serializers; +using mRemoteNG.Connection; +using mRemoteNG.Security; +using mRemoteNG.Tree; +using mRemoteNGTests.TestHelpers; +using NUnit.Framework; + +namespace mRemoteNGTests.Config.Serializers +{ + public class DataTableDeserializerTests + { + private DataTableDeserializer _deserializer; + + [SetUp] + public void Setup() + { + + } + + + [Test] + public void WeCanDeserializeATree() + { + var model = CreateConnectionTreeModel(); + var dataTable = CreateDataTable(model.RootNodes[0]); + _deserializer = new DataTableDeserializer(dataTable); + var output = _deserializer.Deserialize(); + Assert.That(output.GetRecursiveChildList().Count(), Is.EqualTo(model.GetRecursiveChildList().Count())); + } + + [Test] + public void WeCanDeserializeASingleEntry() + { + var dataTable = CreateDataTable(new ConnectionInfo()); + _deserializer = new DataTableDeserializer(dataTable); + var output = _deserializer.Deserialize(); + Assert.That(output.GetRecursiveChildList().Count(), Is.EqualTo(1)); + } + + + private DataTable CreateDataTable(ConnectionInfo tableContent) + { + var serializer = new DataTableSerializer(new SaveFilter()); + return serializer.Serialize(tableContent); + } + + private ConnectionTreeModel CreateConnectionTreeModel() + { + var builder = new ConnectionTreeModelBuilder(); + return builder.Build(); + } + } +} \ No newline at end of file diff --git a/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs b/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs index a8a5f1f92..17bd78031 100644 --- a/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/DataTableSerializerTests.cs @@ -4,6 +4,7 @@ using mRemoteNG.Container; using mRemoteNG.Security; using mRemoteNG.Tree; using mRemoteNG.Tree.Root; +using mRemoteNGTests.TestHelpers; using NUnit.Framework; namespace mRemoteNGTests.Config.Serializers @@ -20,13 +21,6 @@ namespace mRemoteNGTests.Config.Serializers _dataTableSerializer = new DataTableSerializer(_saveFilter); } - [TearDown] - public void Teardown() - { - _saveFilter = null; - _dataTableSerializer = null; - } - [Test] public void AllItemsSerialized() { @@ -35,6 +29,14 @@ namespace mRemoteNGTests.Config.Serializers Assert.That(dataTable.Rows.Count, Is.EqualTo(3)); } + [Test] + public void ReturnsEmptyDataTableWhenGivenEmptyConnectionTreeModel() + { + var model = new ConnectionTreeModel(); + var dataTable = _dataTableSerializer.Serialize(model); + Assert.That(dataTable.Rows.Count, Is.EqualTo(0)); + } + [Test] public void UsernameSerializedWhenSaveSecurityAllowsIt() { @@ -109,20 +111,18 @@ namespace mRemoteNGTests.Config.Serializers Assert.That(dataTable.Rows[0]["InheritUsername"], Is.False); } + [Test] + public void CanSerializeEmptyConnectionInfo() + { + var dataTable = _dataTableSerializer.Serialize(new ConnectionInfo()); + Assert.That(dataTable.Rows.Count, Is.EqualTo(1)); + } + private ConnectionTreeModel CreateConnectionTreeModel() { - var model = new ConnectionTreeModel(); - var root = new RootNodeInfo(RootNodeType.Connection); - var folder1 = new ContainerInfo {Name = "folder1", Username = "user1", Domain = "domain1", Password = "password1"}; - var con1 = new ConnectionInfo {Name = "Con1", Username = "user1", Domain = "domain1", Password = "password1" }; - var con2 = new ConnectionInfo {Name = "Con2", Username = "user2", Domain = "domain2", Password = "password2" }; - - root.AddChild(folder1); - root.AddChild(con2); - folder1.AddChild(con1); - model.AddRootNode(root); - return model; + var builder = new ConnectionTreeModelBuilder(); + return builder.Build(); } } } \ No newline at end of file diff --git a/mRemoteNGTests/TestHelpers/ConnectionTreeModelBuilder.cs b/mRemoteNGTests/TestHelpers/ConnectionTreeModelBuilder.cs new file mode 100644 index 000000000..efe99b634 --- /dev/null +++ b/mRemoteNGTests/TestHelpers/ConnectionTreeModelBuilder.cs @@ -0,0 +1,25 @@ +using mRemoteNG.Connection; +using mRemoteNG.Container; +using mRemoteNG.Tree; +using mRemoteNG.Tree.Root; + +namespace mRemoteNGTests.TestHelpers +{ + public class ConnectionTreeModelBuilder + { + public ConnectionTreeModel Build() + { + var model = new ConnectionTreeModel(); + var root = new RootNodeInfo(RootNodeType.Connection); + var folder1 = new ContainerInfo { Name = "folder1", Username = "user1", Domain = "domain1", Password = "password1" }; + var con1 = new ConnectionInfo { Name = "Con1", Username = "user1", Domain = "domain1", Password = "password1" }; + var con2 = new ConnectionInfo { Name = "Con2", Username = "user2", Domain = "domain2", Password = "password2" }; + + root.AddChild(folder1); + root.AddChild(con2); + folder1.AddChild(con1); + model.AddRootNode(root); + return model; + } + } +} \ No newline at end of file diff --git a/mRemoteNGTests/mRemoteNGTests.csproj b/mRemoteNGTests/mRemoteNGTests.csproj index 7c2310353..3336bf9e4 100644 --- a/mRemoteNGTests/mRemoteNGTests.csproj +++ b/mRemoteNGTests/mRemoteNGTests.csproj @@ -111,6 +111,7 @@ + @@ -129,6 +130,7 @@ + diff --git a/mRemoteV1/App/NativeMethods.cs b/mRemoteV1/App/NativeMethods.cs index 41127e1ee..44aa0b96f 100644 --- a/mRemoteV1/App/NativeMethods.cs +++ b/mRemoteV1/App/NativeMethods.cs @@ -1,6 +1,9 @@ using System; using System.Drawing; +using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; +using System.Text; + #pragma warning disable 169 namespace mRemoteNG.App @@ -42,11 +45,17 @@ namespace mRemoteNG.App internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, System.Text.StringBuilder lParam); + internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, string lParam); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + internal static extern IntPtr SendMessage([In] IntPtr hWnd, [In] uint msg, [Out] StringBuilder wParam, [In] IntPtr lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] internal static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer); - + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] internal static extern bool SetForegroundWindow(IntPtr hWnd); @@ -76,6 +85,10 @@ namespace mRemoteNG.App [DllImport("user32", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)] internal static extern bool SetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + + [DllImport("kernel32", SetLastError = true)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] + internal static extern bool CloseHandle(IntPtr handle); #endregion #region Structures @@ -286,7 +299,10 @@ namespace mRemoteNG.App public const int WA_ACTIVE = 0x1; /// - /// + /// Sent to both the window being activated and the window being deactivated. + /// If the windows use the same input queue, the message is sent synchronously, first to the window procedure of the + /// top-level window being deactivated, then to the window procedure of the top-level window being activated. If the + /// windows use different input queues, the message is sent asynchronously, so the window is activated immediately. /// public const int WA_CLICKACTIVE = 0x2; #endregion @@ -455,6 +471,12 @@ namespace mRemoteNG.App public const int VK_C = 0x67; #endregion + #region EM + public const uint ECM_FIRST = 0x1500; + public const uint EM_SETCUEBANNER = ECM_FIRST + 1; + public const uint EM_GETCUEBANNER = ECM_FIRST + 2; + #endregion + #region LB public const int LB_ERR = -1; public const int LB_SELECTSTRING = 0x18C; diff --git a/mRemoteV1/Config/Putty/AbstractPuttySessionsProvider.cs b/mRemoteV1/Config/Putty/AbstractPuttySessionsProvider.cs index cbd5d443c..dad5a27ca 100644 --- a/mRemoteV1/Config/Putty/AbstractPuttySessionsProvider.cs +++ b/mRemoteV1/Config/Putty/AbstractPuttySessionsProvider.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; +using System.Web; using mRemoteNG.Connection; using mRemoteNG.Tree.Root; @@ -42,7 +43,8 @@ namespace mRemoteNG.Config.Putty private IEnumerable GetSessionToRemove(IEnumerable sessionNamesFromProvider) { var currentlyKnownSessionNames = Sessions.Select(session => session.Name); - var sessionNamesToRemove = currentlyKnownSessionNames.Except(sessionNamesFromProvider); + var normalizedSessionNames = sessionNamesFromProvider.Select(HttpUtility.UrlDecode); + var sessionNamesToRemove = currentlyKnownSessionNames.Except(normalizedSessionNames); return Sessions.Where(session => sessionNamesToRemove.Contains(session.Name)); } diff --git a/mRemoteV1/Config/Putty/PuttySessionsRegistryProvider.cs b/mRemoteV1/Config/Putty/PuttySessionsRegistryProvider.cs index 402a32016..1aa2c931b 100644 --- a/mRemoteV1/Config/Putty/PuttySessionsRegistryProvider.cs +++ b/mRemoteV1/Config/Putty/PuttySessionsRegistryProvider.cs @@ -31,7 +31,7 @@ namespace mRemoteNG.Config.Putty if (raw && !sessionNames.Contains("Default%20Settings")) sessionNames.Insert(0, "Default%20Settings"); - else if (!sessionNames.Contains("Default Settings")) + else if (!raw && !sessionNames.Contains("Default Settings")) sessionNames.Insert(0, "Default Settings"); return sessionNames.ToArray(); diff --git a/mRemoteV1/Config/Serializers/DataTableDeserializer.cs b/mRemoteV1/Config/Serializers/DataTableDeserializer.cs index 5b6b4888d..31df1aa2b 100644 --- a/mRemoteV1/Config/Serializers/DataTableDeserializer.cs +++ b/mRemoteV1/Config/Serializers/DataTableDeserializer.cs @@ -43,10 +43,16 @@ namespace mRemoteNG.Config.Serializers var nodeList = new List(); foreach (DataRow row in _dataTable.Rows) { - if ((string)row["Type"] == "Connection") + // ReSharper disable once SwitchStatementMissingSomeCases + switch ((string)row["Type"]) + { + case "Connection": nodeList.Add(DeserializeConnectionInfo(row)); - else if ((string)row["Type"] == "Container") + break; + case "Container": nodeList.Add(DeserializeContainerInfo(row)); + break; + } } return nodeList; } @@ -69,8 +75,15 @@ namespace mRemoteNG.Config.Serializers { connectionInfo.Name = (string)dataRow["Name"]; connectionInfo.ConstantID = (string)dataRow["ConstantID"]; + + // This throws a NPE - Parent is a connectionInfo object which will be null at this point. + // The Parent object is linked properly later in CreateNodeHierarchy() //connectionInfo.Parent.ConstantID = (string)dataRow["ParentID"]; - //connectionInfo is ContainerInfo ? ((ContainerInfo)connectionInfo).IsExpanded.ToString() : "" = dataRow["Expanded"]; + + var info = connectionInfo as ContainerInfo; + if(info != null) + info.IsExpanded = (bool)dataRow["Expanded"]; + connectionInfo.Description = (string)dataRow["Description"]; connectionInfo.Icon = (string)dataRow["Icon"]; connectionInfo.Panel = (string)dataRow["Panel"]; @@ -195,7 +208,7 @@ namespace mRemoteNG.Config.Serializers var id = (string) row["ConstantID"]; var connectionInfo = connectionList.First(node => node.ConstantID == id); var parentId = (string) row["ParentID"]; - if (parentId == "0") + if (parentId == "0" || connectionList.All(node => node.ConstantID != parentId)) rootNode.AddChild(connectionInfo); else (connectionList.First(node => node.ConstantID == parentId) as ContainerInfo)?.AddChild(connectionInfo); diff --git a/mRemoteV1/Config/Serializers/DataTableSerializer.cs b/mRemoteV1/Config/Serializers/DataTableSerializer.cs index 22a1ceb8f..21ddbee80 100644 --- a/mRemoteV1/Config/Serializers/DataTableSerializer.cs +++ b/mRemoteV1/Config/Serializers/DataTableSerializer.cs @@ -25,147 +25,162 @@ namespace mRemoteNG.Config.Serializers public DataTable Serialize(ConnectionTreeModel connectionTreeModel) { - var rootNode = (RootNodeInfo)connectionTreeModel.RootNodes.First(node => node is RootNodeInfo); - return Serialize(rootNode); + try + { + _dataTable = BuildTable(); + _currentNodeIndex = 0; + var rootNode = connectionTreeModel.RootNodes.First(node => node is RootNodeInfo); + return Serialize(rootNode); + } + catch + { + return _dataTable; + } } public DataTable Serialize(ConnectionInfo serializationTarget) { - _dataTable = new DataTable(TableName); - CreateSchema(); - SetPrimaryKey(); + _dataTable = BuildTable(); _currentNodeIndex = 0; SerializeNodesRecursive(serializationTarget); return _dataTable; } - private void CreateSchema() + private DataTable BuildTable() { - // Note: these columns must be defined in the same order that they exist in the DB - _dataTable.Columns.Add("ID", typeof(int)); - _dataTable.Columns[0].AutoIncrement = true; - _dataTable.Columns.Add("ConstantID", typeof(string)); - _dataTable.Columns.Add("PositionID", typeof(int)); - _dataTable.Columns.Add("ParentID", typeof(string)); - _dataTable.Columns.Add("LastChange", typeof(SqlDateTime)); - _dataTable.Columns.Add("Name", typeof(string)); - _dataTable.Columns.Add("Type", typeof(string)); - _dataTable.Columns.Add("Expanded", typeof(bool)); - _dataTable.Columns.Add("Description", typeof(string)); - _dataTable.Columns.Add("Icon", typeof(string)); - _dataTable.Columns.Add("Panel", typeof(string)); - _dataTable.Columns.Add("Username", typeof(string)); - _dataTable.Columns.Add("DomainName", typeof(string)); - _dataTable.Columns.Add("Password", typeof(string)); - _dataTable.Columns.Add("Hostname", typeof(string)); - _dataTable.Columns.Add("Protocol", typeof(string)); - _dataTable.Columns.Add("PuttySession", typeof(string)); - _dataTable.Columns.Add("Port", typeof(int)); - _dataTable.Columns.Add("ConnectToConsole", typeof(bool)); - _dataTable.Columns.Add("UseCredSsp", typeof(bool)); - _dataTable.Columns.Add("RenderingEngine", typeof(string)); - _dataTable.Columns.Add("ICAEncryptionStrength", typeof(string)); - _dataTable.Columns.Add("RDPAuthenticationLevel", typeof(string)); - _dataTable.Columns.Add("Colors", typeof(string)); - _dataTable.Columns.Add("Resolution", typeof(string)); - _dataTable.Columns.Add("DisplayWallpaper", typeof(bool)); - _dataTable.Columns.Add("DisplayThemes", typeof(bool)); - _dataTable.Columns.Add("EnableFontSmoothing", typeof(bool)); - _dataTable.Columns.Add("EnableDesktopComposition", typeof(bool)); - _dataTable.Columns.Add("CacheBitmaps", typeof(bool)); - _dataTable.Columns.Add("RedirectDiskDrives", typeof(bool)); - _dataTable.Columns.Add("RedirectPorts", typeof(bool)); - _dataTable.Columns.Add("RedirectPrinters", typeof(bool)); - _dataTable.Columns.Add("RedirectSmartCards", typeof(bool)); - _dataTable.Columns.Add("RedirectSound", typeof(string)); - _dataTable.Columns.Add("RedirectKeys", typeof(bool)); - _dataTable.Columns.Add("Connected", typeof(bool)); - _dataTable.Columns.Add("PreExtApp", typeof(string)); - _dataTable.Columns.Add("PostExtApp", typeof(string)); - _dataTable.Columns.Add("MacAddress", typeof(string)); - _dataTable.Columns.Add("UserField", typeof(string)); - _dataTable.Columns.Add("ExtApp", typeof(string)); - _dataTable.Columns.Add("VNCCompression", typeof(string)); - _dataTable.Columns.Add("VNCEncoding", typeof(string)); - _dataTable.Columns.Add("VNCAuthMode", typeof(string)); - _dataTable.Columns.Add("VNCProxyType", typeof(string)); - _dataTable.Columns.Add("VNCProxyIP", typeof(string)); - _dataTable.Columns.Add("VNCProxyPort", typeof(int)); - _dataTable.Columns.Add("VNCProxyUsername", typeof(string)); - _dataTable.Columns.Add("VNCProxyPassword", typeof(string)); - _dataTable.Columns.Add("VNCColors", typeof(string)); - _dataTable.Columns.Add("VNCSmartSizeMode", typeof(string)); - _dataTable.Columns.Add("VNCViewOnly", typeof(bool)); - _dataTable.Columns.Add("RDGatewayUsageMethod", typeof(string)); - _dataTable.Columns.Add("RDGatewayHostname", typeof(string)); - _dataTable.Columns.Add("RDGatewayUseConnectionCredentials", typeof(string)); - _dataTable.Columns.Add("RDGatewayUsername", typeof(string)); - _dataTable.Columns.Add("RDGatewayPassword", typeof(string)); - _dataTable.Columns.Add("RDGatewayDomain", typeof(string)); - _dataTable.Columns.Add("InheritCacheBitmaps", typeof(bool)); - _dataTable.Columns.Add("InheritColors", typeof(bool)); - _dataTable.Columns.Add("InheritDescription", typeof(bool)); - _dataTable.Columns.Add("InheritDisplayThemes", typeof(bool)); - _dataTable.Columns.Add("InheritDisplayWallpaper", typeof(bool)); - _dataTable.Columns.Add("InheritEnableFontSmoothing", typeof(bool)); - _dataTable.Columns.Add("InheritEnableDesktopComposition", typeof(bool)); - _dataTable.Columns.Add("InheritDomain", typeof(bool)); - _dataTable.Columns.Add("InheritIcon", typeof(bool)); - _dataTable.Columns.Add("InheritPanel", typeof(bool)); - _dataTable.Columns.Add("InheritPassword", typeof(bool)); - _dataTable.Columns.Add("InheritPort", typeof(bool)); - _dataTable.Columns.Add("InheritProtocol", typeof(bool)); - _dataTable.Columns.Add("InheritPuttySession", typeof(bool)); - _dataTable.Columns.Add("InheritRedirectDiskDrives", typeof(bool)); - _dataTable.Columns.Add("InheritRedirectKeys", typeof(bool)); - _dataTable.Columns.Add("InheritRedirectPorts", typeof(bool)); - _dataTable.Columns.Add("InheritRedirectPrinters", typeof(bool)); - _dataTable.Columns.Add("InheritRedirectSmartCards", typeof(bool)); - _dataTable.Columns.Add("InheritRedirectSound", typeof(bool)); - _dataTable.Columns.Add("InheritResolution", typeof(bool)); - _dataTable.Columns.Add("InheritUseConsoleSession", typeof(bool)); - _dataTable.Columns.Add("InheritUseCredSsp", typeof(bool)); - _dataTable.Columns.Add("InheritRenderingEngine", typeof(bool)); - _dataTable.Columns.Add("InheritICAEncryptionStrength", typeof(bool)); - _dataTable.Columns.Add("InheritRDPAuthenticationLevel", typeof(bool)); - _dataTable.Columns.Add("InheritUsername", typeof(bool)); - _dataTable.Columns.Add("InheritPreExtApp", typeof(bool)); - _dataTable.Columns.Add("InheritPostExtApp", typeof(bool)); - _dataTable.Columns.Add("InheritMacAddress", typeof(bool)); - _dataTable.Columns.Add("InheritUserField", typeof(bool)); - _dataTable.Columns.Add("InheritExtApp", typeof(bool)); - _dataTable.Columns.Add("InheritVNCCompression", typeof(bool)); - _dataTable.Columns.Add("InheritVNCEncoding", typeof(bool)); - _dataTable.Columns.Add("InheritVNCAuthMode", typeof(bool)); - _dataTable.Columns.Add("InheritVNCProxyType", typeof(bool)); - _dataTable.Columns.Add("InheritVNCProxyIP", typeof(bool)); - _dataTable.Columns.Add("InheritVNCProxyPort", typeof(bool)); - _dataTable.Columns.Add("InheritVNCProxyUsername", typeof(bool)); - _dataTable.Columns.Add("InheritVNCProxyPassword", typeof(bool)); - _dataTable.Columns.Add("InheritVNCColors", typeof(bool)); - _dataTable.Columns.Add("InheritVNCSmartSizeMode", typeof(bool)); - _dataTable.Columns.Add("InheritVNCViewOnly", typeof(bool)); - _dataTable.Columns.Add("InheritRDGatewayUsageMethod", typeof(bool)); - _dataTable.Columns.Add("InheritRDGatewayHostname", typeof(bool)); - _dataTable.Columns.Add("InheritRDGatewayUseConnectionCredentials", typeof(bool)); - _dataTable.Columns.Add("InheritRDGatewayUsername", typeof(bool)); - _dataTable.Columns.Add("InheritRDGatewayPassword", typeof(bool)); - _dataTable.Columns.Add("InheritRDGatewayDomain", typeof(bool)); - _dataTable.Columns.Add("LoadBalanceInfo", typeof(string)); - _dataTable.Columns.Add("AutomaticResize", typeof(bool)); - _dataTable.Columns.Add("InheritLoadBalanceInfo", typeof(bool)); - _dataTable.Columns.Add("InheritAutomaticResize", typeof(bool)); - _dataTable.Columns.Add("RDPMinutesToIdleTimeout", typeof(int)); - _dataTable.Columns.Add("RDPAlertIdleTimeout", typeof(bool)); - _dataTable.Columns.Add("SoundQuality", typeof(string)); - _dataTable.Columns.Add("InheritRDPMinutesToIdleTimeout", typeof(bool)); - _dataTable.Columns.Add("InheritRDPAlertIdleTimeout", typeof(bool)); - _dataTable.Columns.Add("InheritSoundQuality", typeof(bool)); + var dataTable = new DataTable(TableName); + CreateSchema(dataTable); + SetPrimaryKey(dataTable); + return dataTable; } - private void SetPrimaryKey() + private void CreateSchema(DataTable dataTable) { - _dataTable.PrimaryKey = new[] { _dataTable.Columns["ConstantID"] }; + // Note: these columns must be defined in the same order that they exist in the DB + dataTable.Columns.Add("ID", typeof(int)); + dataTable.Columns[0].AutoIncrement = true; + dataTable.Columns.Add("ConstantID", typeof(string)); + dataTable.Columns.Add("PositionID", typeof(int)); + dataTable.Columns.Add("ParentID", typeof(string)); + dataTable.Columns.Add("LastChange", typeof(SqlDateTime)); + dataTable.Columns.Add("Name", typeof(string)); + dataTable.Columns.Add("Type", typeof(string)); + dataTable.Columns.Add("Expanded", typeof(bool)); + dataTable.Columns.Add("Description", typeof(string)); + dataTable.Columns.Add("Icon", typeof(string)); + dataTable.Columns.Add("Panel", typeof(string)); + dataTable.Columns.Add("Username", typeof(string)); + dataTable.Columns.Add("DomainName", typeof(string)); + dataTable.Columns.Add("Password", typeof(string)); + dataTable.Columns.Add("Hostname", typeof(string)); + dataTable.Columns.Add("Protocol", typeof(string)); + dataTable.Columns.Add("PuttySession", typeof(string)); + dataTable.Columns.Add("Port", typeof(int)); + dataTable.Columns.Add("ConnectToConsole", typeof(bool)); + dataTable.Columns.Add("UseCredSsp", typeof(bool)); + dataTable.Columns.Add("RenderingEngine", typeof(string)); + dataTable.Columns.Add("ICAEncryptionStrength", typeof(string)); + dataTable.Columns.Add("RDPAuthenticationLevel", typeof(string)); + dataTable.Columns.Add("Colors", typeof(string)); + dataTable.Columns.Add("Resolution", typeof(string)); + dataTable.Columns.Add("DisplayWallpaper", typeof(bool)); + dataTable.Columns.Add("DisplayThemes", typeof(bool)); + dataTable.Columns.Add("EnableFontSmoothing", typeof(bool)); + dataTable.Columns.Add("EnableDesktopComposition", typeof(bool)); + dataTable.Columns.Add("CacheBitmaps", typeof(bool)); + dataTable.Columns.Add("RedirectDiskDrives", typeof(bool)); + dataTable.Columns.Add("RedirectPorts", typeof(bool)); + dataTable.Columns.Add("RedirectPrinters", typeof(bool)); + dataTable.Columns.Add("RedirectSmartCards", typeof(bool)); + dataTable.Columns.Add("RedirectSound", typeof(string)); + dataTable.Columns.Add("RedirectKeys", typeof(bool)); + dataTable.Columns.Add("Connected", typeof(bool)); + dataTable.Columns.Add("PreExtApp", typeof(string)); + dataTable.Columns.Add("PostExtApp", typeof(string)); + dataTable.Columns.Add("MacAddress", typeof(string)); + dataTable.Columns.Add("UserField", typeof(string)); + dataTable.Columns.Add("ExtApp", typeof(string)); + dataTable.Columns.Add("VNCCompression", typeof(string)); + dataTable.Columns.Add("VNCEncoding", typeof(string)); + dataTable.Columns.Add("VNCAuthMode", typeof(string)); + dataTable.Columns.Add("VNCProxyType", typeof(string)); + dataTable.Columns.Add("VNCProxyIP", typeof(string)); + dataTable.Columns.Add("VNCProxyPort", typeof(int)); + dataTable.Columns.Add("VNCProxyUsername", typeof(string)); + dataTable.Columns.Add("VNCProxyPassword", typeof(string)); + dataTable.Columns.Add("VNCColors", typeof(string)); + dataTable.Columns.Add("VNCSmartSizeMode", typeof(string)); + dataTable.Columns.Add("VNCViewOnly", typeof(bool)); + dataTable.Columns.Add("RDGatewayUsageMethod", typeof(string)); + dataTable.Columns.Add("RDGatewayHostname", typeof(string)); + dataTable.Columns.Add("RDGatewayUseConnectionCredentials", typeof(string)); + dataTable.Columns.Add("RDGatewayUsername", typeof(string)); + dataTable.Columns.Add("RDGatewayPassword", typeof(string)); + dataTable.Columns.Add("RDGatewayDomain", typeof(string)); + dataTable.Columns.Add("InheritCacheBitmaps", typeof(bool)); + dataTable.Columns.Add("InheritColors", typeof(bool)); + dataTable.Columns.Add("InheritDescription", typeof(bool)); + dataTable.Columns.Add("InheritDisplayThemes", typeof(bool)); + dataTable.Columns.Add("InheritDisplayWallpaper", typeof(bool)); + dataTable.Columns.Add("InheritEnableFontSmoothing", typeof(bool)); + dataTable.Columns.Add("InheritEnableDesktopComposition", typeof(bool)); + dataTable.Columns.Add("InheritDomain", typeof(bool)); + dataTable.Columns.Add("InheritIcon", typeof(bool)); + dataTable.Columns.Add("InheritPanel", typeof(bool)); + dataTable.Columns.Add("InheritPassword", typeof(bool)); + dataTable.Columns.Add("InheritPort", typeof(bool)); + dataTable.Columns.Add("InheritProtocol", typeof(bool)); + dataTable.Columns.Add("InheritPuttySession", typeof(bool)); + dataTable.Columns.Add("InheritRedirectDiskDrives", typeof(bool)); + dataTable.Columns.Add("InheritRedirectKeys", typeof(bool)); + dataTable.Columns.Add("InheritRedirectPorts", typeof(bool)); + dataTable.Columns.Add("InheritRedirectPrinters", typeof(bool)); + dataTable.Columns.Add("InheritRedirectSmartCards", typeof(bool)); + dataTable.Columns.Add("InheritRedirectSound", typeof(bool)); + dataTable.Columns.Add("InheritResolution", typeof(bool)); + dataTable.Columns.Add("InheritUseConsoleSession", typeof(bool)); + dataTable.Columns.Add("InheritUseCredSsp", typeof(bool)); + dataTable.Columns.Add("InheritRenderingEngine", typeof(bool)); + dataTable.Columns.Add("InheritICAEncryptionStrength", typeof(bool)); + dataTable.Columns.Add("InheritRDPAuthenticationLevel", typeof(bool)); + dataTable.Columns.Add("InheritUsername", typeof(bool)); + dataTable.Columns.Add("InheritPreExtApp", typeof(bool)); + dataTable.Columns.Add("InheritPostExtApp", typeof(bool)); + dataTable.Columns.Add("InheritMacAddress", typeof(bool)); + dataTable.Columns.Add("InheritUserField", typeof(bool)); + dataTable.Columns.Add("InheritExtApp", typeof(bool)); + dataTable.Columns.Add("InheritVNCCompression", typeof(bool)); + dataTable.Columns.Add("InheritVNCEncoding", typeof(bool)); + dataTable.Columns.Add("InheritVNCAuthMode", typeof(bool)); + dataTable.Columns.Add("InheritVNCProxyType", typeof(bool)); + dataTable.Columns.Add("InheritVNCProxyIP", typeof(bool)); + dataTable.Columns.Add("InheritVNCProxyPort", typeof(bool)); + dataTable.Columns.Add("InheritVNCProxyUsername", typeof(bool)); + dataTable.Columns.Add("InheritVNCProxyPassword", typeof(bool)); + dataTable.Columns.Add("InheritVNCColors", typeof(bool)); + dataTable.Columns.Add("InheritVNCSmartSizeMode", typeof(bool)); + dataTable.Columns.Add("InheritVNCViewOnly", typeof(bool)); + dataTable.Columns.Add("InheritRDGatewayUsageMethod", typeof(bool)); + dataTable.Columns.Add("InheritRDGatewayHostname", typeof(bool)); + dataTable.Columns.Add("InheritRDGatewayUseConnectionCredentials", typeof(bool)); + dataTable.Columns.Add("InheritRDGatewayUsername", typeof(bool)); + dataTable.Columns.Add("InheritRDGatewayPassword", typeof(bool)); + dataTable.Columns.Add("InheritRDGatewayDomain", typeof(bool)); + dataTable.Columns.Add("LoadBalanceInfo", typeof(string)); + dataTable.Columns.Add("AutomaticResize", typeof(bool)); + dataTable.Columns.Add("InheritLoadBalanceInfo", typeof(bool)); + dataTable.Columns.Add("InheritAutomaticResize", typeof(bool)); + dataTable.Columns.Add("RDPMinutesToIdleTimeout", typeof(int)); + dataTable.Columns.Add("RDPAlertIdleTimeout", typeof(bool)); + dataTable.Columns.Add("SoundQuality", typeof(string)); + dataTable.Columns.Add("InheritRDPMinutesToIdleTimeout", typeof(bool)); + dataTable.Columns.Add("InheritRDPAlertIdleTimeout", typeof(bool)); + dataTable.Columns.Add("InheritSoundQuality", typeof(bool)); + } + + private void SetPrimaryKey(DataTable dataTable) + { + dataTable.PrimaryKey = new[] { dataTable.Columns["ConstantID"] }; } private void SerializeNodesRecursive(ConnectionInfo connectionInfo) @@ -186,7 +201,7 @@ namespace mRemoteNG.Config.Serializers dataRow["Name"] = connectionInfo.Name; dataRow["Type"] = connectionInfo.GetTreeNodeType().ToString(); dataRow["ConstantID"] = connectionInfo.ConstantID; - dataRow["ParentID"] = connectionInfo.Parent.ConstantID; + dataRow["ParentID"] = connectionInfo.Parent?.ConstantID ?? ""; dataRow["PositionID"] = _currentNodeIndex; dataRow["LastChange"] = (SqlDateTime)DateTime.Now; var info = connectionInfo as ContainerInfo; diff --git a/mRemoteV1/Connection/ConnectionInfo.cs b/mRemoteV1/Connection/ConnectionInfo.cs index 8d00dec50..76b006ae7 100644 --- a/mRemoteV1/Connection/ConnectionInfo.cs +++ b/mRemoteV1/Connection/ConnectionInfo.cs @@ -172,7 +172,7 @@ namespace mRemoteNG.Connection { var inheritType = Inheritance.GetType(); var inheritPropertyInfo = inheritType.GetProperty(propertyName); - var inheritPropertyValue = Convert.ToBoolean(inheritPropertyInfo.GetValue(Inheritance, null)); + var inheritPropertyValue = inheritPropertyInfo != null && Convert.ToBoolean(inheritPropertyInfo.GetValue(Inheritance, null)); return inheritPropertyValue; } @@ -180,15 +180,20 @@ namespace mRemoteNG.Connection { var connectionInfoType = Parent.GetType(); var parentPropertyInfo = connectionInfoType.GetProperty(propertyName); + if (parentPropertyInfo == null) + return default(TPropertyType); // shouldn't get here... var parentPropertyValue = (TPropertyType)parentPropertyInfo.GetValue(Parent, null); return parentPropertyValue; + + } private static int GetDefaultPort(ProtocolType protocol) { try { + // ReSharper disable once SwitchStatementMissingSomeCases switch (protocol) { case ProtocolType.RDP: diff --git a/mRemoteV1/Properties/AssemblyInfo.cs b/mRemoteV1/Properties/AssemblyInfo.cs index 47f6b26ce..263bd9277 100644 --- a/mRemoteV1/Properties/AssemblyInfo.cs +++ b/mRemoteV1/Properties/AssemblyInfo.cs @@ -33,7 +33,7 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // -[assembly: AssemblyVersion("1.75.7003.*")] +[assembly: AssemblyVersion("1.75.7005.*")] [assembly:NeutralResourcesLanguageAttribute("en")] diff --git a/mRemoteV1/Resources/PuTTYNG.exe b/mRemoteV1/Resources/PuTTYNG.exe index 5749a78b6..0d9c7fc32 100644 Binary files a/mRemoteV1/Resources/PuTTYNG.exe and b/mRemoteV1/Resources/PuTTYNG.exe differ diff --git a/mRemoteV1/Tools/Tools.SystemMenu.cs b/mRemoteV1/Tools/Tools.SystemMenu.cs index fdee011fe..227c242a7 100644 --- a/mRemoteV1/Tools/Tools.SystemMenu.cs +++ b/mRemoteV1/Tools/Tools.SystemMenu.cs @@ -1,54 +1,92 @@ using System; using System.Drawing; - +using mRemoteNG.App; +using Microsoft.Win32.SafeHandles; +// ReSharper disable MemberCanBeMadeStatic.Global namespace mRemoteNG.Tools { - public class SystemMenu - { + public sealed class SystemMenu : SafeHandleZeroOrMinusOneIsInvalid, IDisposable + { [Flags] public enum Flags { - MF_STRING = App.NativeMethods.MF_STRING, - MF_SEPARATOR = App.NativeMethods.MF_SEPARATOR, - MF_BYCOMMAND = App.NativeMethods.MF_BYCOMMAND, - MF_BYPOSITION = App.NativeMethods.MF_BYPOSITION, - MF_POPUP = App.NativeMethods.MF_POPUP, - WM_SYSCOMMAND = App.NativeMethods.WM_SYSCOMMAND + MF_STRING = NativeMethods.MF_STRING, + MF_SEPARATOR = NativeMethods.MF_SEPARATOR, + MF_BYCOMMAND = NativeMethods.MF_BYCOMMAND, + MF_BYPOSITION = NativeMethods.MF_BYPOSITION, + MF_POPUP = NativeMethods.MF_POPUP, + WM_SYSCOMMAND = NativeMethods.WM_SYSCOMMAND } - - public IntPtr SystemMenuHandle; - public IntPtr FormHandle; - - public SystemMenu(IntPtr Handle) + + private bool disposed; + internal IntPtr SystemMenuHandle; + private readonly IntPtr FormHandle; + + public SystemMenu(IntPtr Handle) :base(true) { FormHandle = Handle; - SystemMenuHandle = App.NativeMethods.GetSystemMenu(FormHandle, false); - } + SystemMenuHandle = NativeMethods.GetSystemMenu(FormHandle, false); + SetHandle(SystemMenuHandle); + } public void Reset() { - SystemMenuHandle = App.NativeMethods.GetSystemMenu(FormHandle, true); + SystemMenuHandle = NativeMethods.GetSystemMenu(FormHandle, true); } public void AppendMenuItem(IntPtr ParentMenu, Flags Flags, IntPtr ID, string Text) { - App.NativeMethods.AppendMenu(ParentMenu, (int)Flags, ID, Text); + NativeMethods.AppendMenu(ParentMenu, (int)Flags, ID, Text); } public IntPtr CreatePopupMenuItem() { - return App.NativeMethods.CreatePopupMenu(); + return NativeMethods.CreatePopupMenu(); } public bool InsertMenuItem(IntPtr SysMenu, int Position, Flags Flags, IntPtr SubMenu, string Text) { - return App.NativeMethods.InsertMenu(SysMenu, Position, (int)Flags, SubMenu, Text); + return NativeMethods.InsertMenu(SysMenu, Position, (int)Flags, SubMenu, Text); } public IntPtr SetBitmap(IntPtr Menu, int Position, Flags Flags, Bitmap Bitmap) { - return new IntPtr(Convert.ToInt32(App.NativeMethods.SetMenuItemBitmaps(Menu, Position, (int)Flags, Bitmap.GetHbitmap(), Bitmap.GetHbitmap()))); + return new IntPtr(Convert.ToInt32(NativeMethods.SetMenuItemBitmaps(Menu, Position, (int)Flags, Bitmap.GetHbitmap(), Bitmap.GetHbitmap()))); } - } + + protected override bool ReleaseHandle() + { + return NativeMethods.CloseHandle(SystemMenuHandle); + } + + + /* If we don't have the finalizer, then we get this warning: https://msdn.microsoft.com/library/ms182329.aspx (CA2216: Disposable types should declare finalizer) + * If we DO have the finalizer, then we get this warning: https://msdn.microsoft.com/library/ms244737.aspx (CA1063: Implement IDisposable correctly) + * + * Since the handle is likely going to be in use for the entierty of the process, the finalizer isn't very important since when we're calling it + * the process is likely exiting. Leaks would be moot once it exits. CA2216 is the lesser of 2 evils as far as I can tell. Suppress. + ~SystemMenu() + { + Dispose(false); + } + */ + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2216:DisposableTypesShouldDeclareFinalizer")] + public new void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected override void Dispose(bool disposing) + { + if (disposed) return; + if (!disposing) return; + + ReleaseHandle(); + + disposed = true; + } + } } \ No newline at end of file diff --git a/mRemoteV1/UI/Controls/ConnectionContextMenu.cs b/mRemoteV1/UI/Controls/ConnectionContextMenu.cs index 9ff0f1457..fd51d65c2 100644 --- a/mRemoteV1/UI/Controls/ConnectionContextMenu.cs +++ b/mRemoteV1/UI/Controls/ConnectionContextMenu.cs @@ -717,7 +717,11 @@ namespace mRemoteNG.UI.Controls private void OnImportFileClicked(object sender, EventArgs e) { - var selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo ?? _connectionTree.SelectedNode.Parent; + ContainerInfo selectedNodeAsContainer; + if (_connectionTree.SelectedNode == null) + selectedNodeAsContainer = Runtime.ConnectionTreeModel.RootNodes.First(); + else + selectedNodeAsContainer = _connectionTree.SelectedNode as ContainerInfo ?? _connectionTree.SelectedNode.Parent; Import.ImportFromFile(selectedNodeAsContainer); }