diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 71938b95..69d0c051 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -9,6 +9,7 @@ Removed invalid "Site" configuration option from PuTTY Saved Sessions. Fixed PuTTY Saved Sessions still showing if all saved sessions are removed. Fixed config panel showing settings from previously loaded connection file after loading a new one. + Improved handling of variables in external tool arguments. 1.72 (2013-11-13): Fixed issue MR-592 - Unable to run VBS script as an external tool diff --git a/Local.testsettings b/Local.testsettings new file mode 100644 index 00000000..01fa5135 --- /dev/null +++ b/Local.testsettings @@ -0,0 +1,10 @@ + + + These are default test settings for a local test run. + + + + + + + \ No newline at end of file diff --git a/TestProject/ExternalToolTest.vb b/TestProject/ExternalToolTest.vb new file mode 100644 index 00000000..24785c47 --- /dev/null +++ b/TestProject/ExternalToolTest.vb @@ -0,0 +1,145 @@ +Imports Microsoft.VisualStudio.TestTools.UnitTesting + +Imports mRemoteNG.Tools +Imports mRemoteNG.Connection.PuttySession + + +''' +'''This is a test class for ExternalToolTest and is intended +'''to contain all ExternalToolTest Unit Tests +''' + _ +Public Class ExternalToolTest + ''' + '''Gets or sets the test context which provides + '''information about and functionality for the current test run. + ''' + Public Property TestContext() As TestContext + +#Region "Additional test attributes" + ' + 'You can use the following additional attributes as you write your tests: + ' + 'Use ClassInitialize to run code before running the first test in the class + ' _ + 'Public Shared Sub MyClassInitialize(ByVal testContext As TestContext) + 'End Sub + ' + 'Use ClassCleanup to run code after all tests in a class have run + ' _ + 'Public Shared Sub MyClassCleanup() + 'End Sub + ' + 'Use TestInitialize to run code before running each test + ' _ + 'Public Sub MyTestInitialize() + 'End Sub + ' + 'Use TestCleanup to run code after each test has run + ' _ + 'Public Sub MyTestCleanup() + 'End Sub + ' +#End Region + + + ''' + '''A test for ParseArguments + ''' + _ + Public Sub ParseArgumentsTest() + Dim externalTool As New ExternalTool + + ' ReSharper disable StringLiteralTypo + externalTool.ConnectionInfo = New Info() + With externalTool.ConnectionInfo + .Name = "EMAN" + .Hostname = "EMANTSOH" + .Port = 9876 + .Username = "EMANRESU" + .Password = "DROWSSAP" + .Domain = "NIAMOD" + .Description = "NOITPIRCSED" + .MacAddress = "SSERDDACAM" + .UserField = "DLEIFRESU" + End With + + Assert.AreEqual("EMAN, EMANTSOH, 9876, EMANRESU, DROWSSAP, NIAMOD, NOITPIRCSED, SSERDDACAM, DLEIFRESU", externalTool.ParseArguments("%NAME%, %HOSTNAME%, %PORT%, %USERNAME%, %PASSWORD%, %DOMAIN%, %DESCRIPTION%, %MACADDRESS%, %USERFIELD%")) + ' ReSharper restore StringLiteralTypo + + Assert.AreEqual(Environment.ExpandEnvironmentVariables("%PATH%"), externalTool.ParseArguments("%!PATH%")) + + externalTool.ConnectionInfo.Name = "()%!^""<>&|\""\\" + + Assert.AreEqual("%%", externalTool.ParseArguments("%%")) + Assert.AreEqual("% %", externalTool.ParseArguments("% %")) + Assert.AreEqual("%-%", externalTool.ParseArguments("%-%")) + Assert.AreEqual("%!%", externalTool.ParseArguments("%!%")) + Assert.AreEqual("%^%", externalTool.ParseArguments("%^%")) + Assert.AreEqual("%%%", externalTool.ParseArguments("%%%")) + Assert.AreEqual("%foobar%", externalTool.ParseArguments("%foobar%")) + Assert.AreEqual("%-foobar%", externalTool.ParseArguments("%-foobar%")) + Assert.AreEqual("%!foobar%", externalTool.ParseArguments("%!foobar%")) + Assert.AreEqual("%-!^\", externalTool.ParseArguments("%-!^\")) + + Assert.AreEqual("^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("%NAME%")) + Assert.AreEqual("^(^)^%^!^^^""^<^>^&^|\^""\\", externalTool.ParseArguments("%-NAME%")) + Assert.AreEqual("()%!^""<>&|\""\\", externalTool.ParseArguments("%!NAME%")) + + Assert.AreEqual("^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("%name%")) + Assert.AreEqual("%^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("%%name%")) + Assert.AreEqual("^(^)^%^!^^\^""^<^>^&^|\\\^""\\%", externalTool.ParseArguments("%name%%")) + Assert.AreEqual("^(^)^%^!^^\^""^<^>^&^|\\\^""\\ ^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("%name% %name%")) + + Assert.AreEqual("%-^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("%-%name%")) + Assert.AreEqual("^(^)^%^!^^\^""^<^>^&^|\\\^""\\-%", externalTool.ParseArguments("%name%-%")) + Assert.AreEqual("%!^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("%!%name%")) + Assert.AreEqual("^(^)^%^!^^\^""^<^>^&^|\\\^""\\!%", externalTool.ParseArguments("%name%!%")) + + Assert.AreEqual("\^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("\%NAME%")) + Assert.AreEqual("\^(^)^%^!^^\^""^<^>^&^|\\\^""\\NAME%", externalTool.ParseArguments("\%NAME%NAME%")) + Assert.AreEqual("%NAME\%", externalTool.ParseArguments("%NAME\%")) + + Assert.AreEqual("""^(^)^%^!^^\^""^<^>^&^|\\\^""\\\\""", externalTool.ParseArguments("""%NAME%""")) + Assert.AreEqual("""^(^)^%^!^^^""^<^>^&^|\^""\\""", externalTool.ParseArguments("""%-NAME%""")) + Assert.AreEqual("""()%!^""<>&|\""\\""", externalTool.ParseArguments("""%!NAME%""")) + + Assert.AreEqual("""^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("""%NAME%")) + Assert.AreEqual("""^(^)^%^!^^^""^<^>^&^|\^""\\", externalTool.ParseArguments("""%-NAME%")) + Assert.AreEqual("""()%!^""<>&|\""\\", externalTool.ParseArguments("""%!NAME%")) + + Assert.AreEqual("^(^)^%^!^^\^""^<^>^&^|\\\^""\\\\""", externalTool.ParseArguments("%NAME%""")) + Assert.AreEqual("^(^)^%^!^^^""^<^>^&^|\^""\\""", externalTool.ParseArguments("%-NAME%""")) + Assert.AreEqual("()%!^""<>&|\""\\""", externalTool.ParseArguments("%!NAME%""")) + + Assert.AreEqual(Environment.ExpandEnvironmentVariables("%USERNAME%"), externalTool.ParseArguments("\%USERNAME\%")) + Assert.AreEqual(Environment.ExpandEnvironmentVariables("%USERNAME%"), externalTool.ParseArguments("\%-USERNAME\%")) + Assert.AreEqual(Environment.ExpandEnvironmentVariables("%USERNAME%"), externalTool.ParseArguments("\%!USERNAME\%")) + + Assert.AreEqual("\^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("\%NAME%")) + Assert.AreEqual("%NAME\%", externalTool.ParseArguments("%NAME\%")) + Assert.AreEqual("\\%NAME\\%", externalTool.ParseArguments("\\%NAME\\%")) + Assert.AreEqual("\\\%NAME\\\%", externalTool.ParseArguments("\\\%NAME\\\%")) + Assert.AreEqual("\\\\%NAME\\\\%", externalTool.ParseArguments("\\\\%NAME\\\\%")) + Assert.AreEqual("\\\\\%NAME\\\\\%", externalTool.ParseArguments("\\\\\%NAME\\\\\%")) + + Assert.AreEqual("%NAME%", externalTool.ParseArguments("^%NAME^%")) + Assert.AreEqual("%-NAME%", externalTool.ParseArguments("^%-NAME^%")) + Assert.AreEqual("\%NAME\%", externalTool.ParseArguments("\^%NAME\^%")) + Assert.AreEqual("\%-NAME\%", externalTool.ParseArguments("\^%-NAME\^%")) + + Assert.AreEqual("^%NAME^%", externalTool.ParseArguments("^^%NAME^^%")) + Assert.AreEqual("^%-NAME^%", externalTool.ParseArguments("^^%-NAME^^%")) + Assert.AreEqual("^^^^%NAME^^^^%", externalTool.ParseArguments("^^^^^%NAME^^^^^%")) + Assert.AreEqual("^^^^%-NAME^^^^%", externalTool.ParseArguments("^^^^^%-NAME^^^^^%")) + Assert.AreEqual("^^^^%!NAME^^^^%", externalTool.ParseArguments("^^^^^%!NAME^^^^^%")) + + Assert.AreEqual("blah%blah", externalTool.ParseArguments("blah%blah")) + Assert.AreEqual("blah^%blah", externalTool.ParseArguments("blah^%blah")) + Assert.AreEqual("blah^^%blah", externalTool.ParseArguments("blah^^%blah")) + Assert.AreEqual("blah^^^%blah", externalTool.ParseArguments("blah^^^%blah")) + + Assert.AreEqual("^^^^%-NAME^^^^% ^(^)^%^!^^\^""^<^>^&^|\\\^""\\", externalTool.ParseArguments("^^^^^%-NAME^^^^^% %NAME%")) + End Sub +End Class diff --git a/TestProject/My Project/AssemblyInfo.vb b/TestProject/My Project/AssemblyInfo.vb new file mode 100644 index 00000000..4546dd89 --- /dev/null +++ b/TestProject/My Project/AssemblyInfo.vb @@ -0,0 +1,37 @@ +Imports System +Imports System.Reflection +Imports System.Runtime.InteropServices + +' General Information about an assembly is controlled through the following +' set of attributes. Change these attribute values to modify the information +' associated with an assembly. + +' Review the values of the assembly attributes + + + + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' Version information for an assembly consists of the following four values: +' +' Major Version +' Minor Version +' Build Number +' Revision +' +' You can specify all the values or you can default the Build and Revision Numbers +' by using the '*' as shown below: +' + + + diff --git a/TestProject/Test References/mRemoteNG.accessor b/TestProject/Test References/mRemoteNG.accessor new file mode 100644 index 00000000..e57ac164 --- /dev/null +++ b/TestProject/Test References/mRemoteNG.accessor @@ -0,0 +1,2 @@ +mRemoteNG.exe +Desktop diff --git a/TestProject/TestProject.vbproj b/TestProject/TestProject.vbproj new file mode 100644 index 00000000..b6834be5 --- /dev/null +++ b/TestProject/TestProject.vbproj @@ -0,0 +1,181 @@ + + + + Debug + AnyCPU + + + + + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97} + Library + TestProject + TestProject + 512 + Windows + v4.0 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} + $(DevEnvDir)PublicAssemblies\ + + + true + full + true + true + bin\Debug\ + TestProject.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + TestProject.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + On + + + Binary + + + Off + + + On + + + + ..\mRemoteV1\References\ADTree.dll + + + ..\mRemoteV1\obj\Debug\AxInterop.MSTSCLib.dll + + + ..\mRemoteV1\obj\Debug\AxInterop.WFICALib.dll + + + ..\mRemoteV1\References\DiffieHellman.dll + + + ..\mRemoteV1\References\FilteredPropertyGrid.dll + + + ..\mRemoteV1\obj\Debug\Interop.EOLWTSCOM.dll + True + True + + + ..\mRemoteV1\obj\Debug\Interop.MSTSCLib.dll + True + True + + + ..\mRemoteV1\obj\Debug\Interop.SHDocVw.dll + True + True + + + ..\mRemoteV1\obj\Debug\Interop.WFICALib.dll + True + True + + + ..\mRemoteV1\References\IPTextBox.dll + + + ..\mRemoteV1\References\log4net.dll + + + True + + + + + ..\mRemoteV1\References\MiniGeckoBrowser.dll + + + ..\mRemoteV1\References\MiniTabControl.dll + + + ..\mRemoteV1\References\Org.Mentalis.Security.dll + + + ..\mRemoteV1\References\PSTaskDialog.dll + + + ..\mRemoteV1\References\Skybound.Gecko.dll + + + True + + + + + 3.5 + + + + + + + + + + + + ..\mRemoteV1\References\Tamir.SharpSSH.dll + + + ..\mRemoteV1\References\VncSharpNG.dll + + + ..\mRemoteV1\References\WeifenLuo.WinFormsUI.Docking.dll + + + + + + + + + + + + + + + + + False + + + + + + + + + + + + {0F615504-5F30-4CF2-8341-1DE7FEC95A23} + SharedLibraryNG + + + {4934A491-40BC-4E5B-9166-EA1169A220F6} + mRemoteV1 + + + + + \ No newline at end of file diff --git a/TraceAndTestImpact.testsettings b/TraceAndTestImpact.testsettings new file mode 100644 index 00000000..dfb8bd7f --- /dev/null +++ b/TraceAndTestImpact.testsettings @@ -0,0 +1,19 @@ + + + These are test settings for Trace and Test Impact. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mRemoteV1.sln b/mRemoteV1.sln index 6814e5e5..481ceeda 100644 --- a/mRemoteV1.sln +++ b/mRemoteV1.sln @@ -5,7 +5,19 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "mRemoteV1", "mRemoteV1\mRem EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedLibraryNG", "..\SharedLibraryNG\SharedLibraryNG\SharedLibraryNG.csproj", "{0F615504-5F30-4CF2-8341-1DE7FEC95A23}" EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "TestProject", "TestProject\TestProject.vbproj", "{24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{38EB1EFD-C8C8-49A2-BCA7-63F7A02B3153}" + ProjectSection(SolutionItems) = preProject + Local.testsettings = Local.testsettings + mRemoteV1.vsmdi = mRemoteV1.vsmdi + TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings + EndProjectSection +EndProject Global + GlobalSection(TestCaseManagementSettings) = postSolution + CategoryFile = mRemoteV1.vsmdi + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug Portable|Any CPU = Debug Portable|Any CPU Debug|Any CPU = Debug|Any CPU @@ -29,6 +41,14 @@ Global {0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release Portable|Any CPU.Build.0 = Release|Any CPU {0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release|Any CPU.ActiveCfg = Release|Any CPU {0F615504-5F30-4CF2-8341-1DE7FEC95A23}.Release|Any CPU.Build.0 = Release|Any CPU + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}.Debug Portable|Any CPU.ActiveCfg = Debug|Any CPU + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}.Debug Portable|Any CPU.Build.0 = Debug|Any CPU + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}.Release Portable|Any CPU.ActiveCfg = Release|Any CPU + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}.Release Portable|Any CPU.Build.0 = Release|Any CPU + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24E0689D-95C0-4AAB-A93F-B8D6B7F0CB97}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/mRemoteV1.vsmdi b/mRemoteV1.vsmdi new file mode 100644 index 00000000..d0d68615 --- /dev/null +++ b/mRemoteV1.vsmdi @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/mRemoteV1/App/App.Runtime.vb b/mRemoteV1/App/App.Runtime.vb index c6a69fad..f9a6a932 100644 --- a/mRemoteV1/App/App.Runtime.vb +++ b/mRemoteV1/App/App.Runtime.vb @@ -1731,7 +1731,7 @@ Namespace App Case Protocol.Protocols.ICA newProtocol = New Protocol.ICA Case Protocol.Protocols.IntApp - newProtocol = New Protocol.IntApp + newProtocol = New Protocol.IntegratedProgram If newConnectionInfo.ExtApp = "" Then Throw New Exception(My.Language.strNoExtAppDefined) @@ -1908,18 +1908,18 @@ Namespace App #Region "External Apps" Public Shared Sub GetExtApps() - Array.Clear(Tools.ExternalAppsTypeConverter.ExternalApps, 0, Tools.ExternalAppsTypeConverter.ExternalApps.Length) - Array.Resize(Tools.ExternalAppsTypeConverter.ExternalApps, ExternalTools.Count + 1) + Array.Clear(Tools.ExternalToolsTypeConverter.ExternalTools, 0, Tools.ExternalToolsTypeConverter.ExternalTools.Length) + Array.Resize(Tools.ExternalToolsTypeConverter.ExternalTools, ExternalTools.Count + 1) Dim i As Integer = 0 For Each extA As Tools.ExternalTool In ExternalTools - Tools.ExternalAppsTypeConverter.ExternalApps(i) = extA.DisplayName + Tools.ExternalToolsTypeConverter.ExternalTools(i) = extA.DisplayName i += 1 Next - Tools.ExternalAppsTypeConverter.ExternalApps(i) = "" + Tools.ExternalToolsTypeConverter.ExternalTools(i) = "" End Sub Public Shared Function GetExtAppByName(ByVal Name As String) As Tools.ExternalTool diff --git a/mRemoteV1/Connection/Connection.Info.vb b/mRemoteV1/Connection/Connection.Info.vb index e0580085..f0c06075 100644 --- a/mRemoteV1/Connection/Connection.Info.vb +++ b/mRemoteV1/Connection/Connection.Info.vb @@ -230,7 +230,7 @@ Namespace Connection Browsable(True), _ LocalizedDisplayName("strPropertyNameExternalTool"), _ LocalizedDescription("strPropertyDescriptionExternalTool"), _ - TypeConverter(GetType(Tools.ExternalAppsTypeConverter))> _ + TypeConverter(GetType(Tools.ExternalToolsTypeConverter))> _ Public Property ExtApp() As String Get If Me._Inherit.ExtApp And Me._Parent IsNot Nothing Then @@ -1012,7 +1012,7 @@ Namespace Connection Browsable(True), _ LocalizedDisplayName("strPropertyNameExternalToolBefore"), _ LocalizedDescription("strPropertyDescriptionExternalToolBefore"), _ - TypeConverter(GetType(Tools.ExternalAppsTypeConverter))> _ + TypeConverter(GetType(Tools.ExternalToolsTypeConverter))> _ Public Overridable Property PreExtApp() As String Get If Me._Inherit.PreExtApp And Me._Parent IsNot Nothing Then @@ -1039,7 +1039,7 @@ Namespace Connection Browsable(True), _ LocalizedDisplayName("strPropertyNameExternalToolAfter"), _ LocalizedDescription("strPropertyDescriptionExternalToolAfter"), _ - TypeConverter(GetType(Tools.ExternalAppsTypeConverter))> _ + TypeConverter(GetType(Tools.ExternalToolsTypeConverter))> _ Public Overridable Property PostExtApp() As String Get If Me._Inherit.PostExtApp And Me._Parent IsNot Nothing Then @@ -1581,7 +1581,7 @@ Namespace Connection Case Connection.Protocol.Protocols.ICA Return Connection.Protocol.ICA.Defaults.Port Case Connection.Protocol.Protocols.IntApp - Return Connection.Protocol.IntApp.Defaults.Port + Return Connection.Protocol.IntegratedProgram.Defaults.Port End Select Catch ex As Exception MessageCollector.AddExceptionMessage(My.Language.strConnectionSetDefaultPortFailed, ex, Messages.MessageClass.ErrorMsg) diff --git a/mRemoteV1/Connection/Connection.Protocol.IntApp.vb b/mRemoteV1/Connection/Connection.Protocol.IntApp.vb deleted file mode 100644 index f54b11ee..00000000 --- a/mRemoteV1/Connection/Connection.Protocol.IntApp.vb +++ /dev/null @@ -1,186 +0,0 @@ -Imports mRemoteNG.App.Native -Imports System.Threading -Imports mRemoteNG.App.Runtime -Imports mRemoteNG.Tools - -Namespace Connection - Namespace Protocol - Public Class IntApp - Inherits Connection.Protocol.Base - -#Region "Private Properties" - Private IntAppProcessStartInfo As New ProcessStartInfo() - Private Arguments As String - Private ExtApp As Tools.ExternalTool -#End Region - -#Region "Public Properties" - Private _IntAppHandle As IntPtr - Public Property IntAppHandle() As IntPtr - Get - Return Me._IntAppHandle - End Get - Set(ByVal value As IntPtr) - Me._IntAppHandle = value - End Set - End Property - - Private _IntAppProcess As Process - Public Property IntAppProcess() As Process - Get - Return Me._IntAppProcess - End Get - Set(ByVal value As Process) - Me._IntAppProcess = value - End Set - End Property - - Private _IntAppPath As String - Public Property IntAppPath() As String - Get - Return _IntAppPath - End Get - Set(ByVal value As String) - _IntAppPath = value - End Set - End Property - - Public ReadOnly Property Focused() As Boolean - Get - If GetForegroundWindow() = IntAppHandle Then - Return True - Else - Return False - End If - End Get - End Property -#End Region - -#Region "Private Events & Handlers" - Private Sub ProcessExited(ByVal sender As Object, ByVal e As System.EventArgs) - MyBase.Event_Closed(Me) - End Sub -#End Region - -#Region "Public Methods" - Public Sub New() - - End Sub - - Public Overrides Function SetProps() As Boolean - ExtApp = App.Runtime.GetExtAppByName(InterfaceControl.Info.ExtApp) - If InterfaceControl.Info IsNot Nothing Then - ExtApp.ConnectionInfo = InterfaceControl.Info - End If - - _IntAppPath = ExtApp.ParseText(ExtApp.FileName) - Arguments = ExtApp.ParseText(ExtApp.Arguments) - - Return MyBase.SetProps() - End Function - - Public Overrides Function Connect() As Boolean - Try - If ExtApp.TryIntegrate = False Then - ExtApp.Start(Me.InterfaceControl.Info) - Me.Close() - Return Nothing - End If - - IntAppProcessStartInfo.UseShellExecute = True - IntAppProcessStartInfo.FileName = _IntAppPath - IntAppProcessStartInfo.Arguments = Arguments - - IntAppProcess = Process.Start(IntAppProcessStartInfo) - IntAppProcess.EnableRaisingEvents = True - IntAppProcess.WaitForInputIdle() - - AddHandler IntAppProcess.Exited, AddressOf ProcessExited - - Dim TryCount As Integer = 0 - Do Until IntAppProcess.MainWindowHandle <> IntPtr.Zero And Me.InterfaceControl.Handle <> IntPtr.Zero And Me.IntAppProcess.MainWindowTitle <> "Default IME" - If TryCount >= My.Settings.MaxPuttyWaitTime * 2 Then - Exit Do - End If - - IntAppProcess.Refresh() - - Thread.Sleep(500) - - TryCount += 1 - Loop - - IntAppHandle = IntAppProcess.MainWindowHandle - - MessageCollector.AddMessage(Messages.MessageClass.InformationMsg, My.Language.strIntAppStuff, True) - - MessageCollector.AddMessage(Messages.MessageClass.InformationMsg, String.Format(My.Language.strIntAppHandle, IntAppHandle.ToString), True) - MessageCollector.AddMessage(Messages.MessageClass.InformationMsg, String.Format(My.Language.strIntAppTitle, IntAppProcess.MainWindowTitle), True) - MessageCollector.AddMessage(Messages.MessageClass.InformationMsg, String.Format(My.Language.strIntAppParentHandle, Me.InterfaceControl.Parent.Handle.ToString), True) - - SetParent(Me.IntAppHandle, Me.InterfaceControl.Parent.Handle) - SetWindowLong(Me.IntAppHandle, 0, WS_VISIBLE) - ShowWindow(Me.IntAppHandle, SW_SHOWMAXIMIZED) - - Resize(Me, New EventArgs) - - MyBase.Connect() - Return True - Catch ex As Exception - MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg, My.Language.strIntAppConnectionFailed & vbNewLine & ex.Message) - Return False - End Try - End Function - - - Public Overrides Sub Focus() - Try - SetForegroundWindow(IntAppHandle) - Catch ex As Exception - MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg, My.Language.strIntAppFocusFailed & vbNewLine & ex.Message, True) - End Try - End Sub - - Public Overrides Sub Resize(ByVal sender As Object, ByVal e As EventArgs) - Try - If InterfaceControl.Size = Size.Empty Then Return - MoveWindow(IntAppHandle, -SystemInformation.FrameBorderSize.Width, -(SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height), Me.InterfaceControl.Width + (SystemInformation.FrameBorderSize.Width * 2), Me.InterfaceControl.Height + SystemInformation.CaptionHeight + (SystemInformation.FrameBorderSize.Height * 2), True) - Catch ex As Exception - MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg, My.Language.strIntAppResizeFailed & vbNewLine & ex.Message, True) - End Try - End Sub - - Public Overrides Sub Close() - Try - If IntAppProcess.HasExited = False Then - IntAppProcess.Kill() - End If - Catch ex As Exception - MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg, My.Language.strIntAppKillFailed & vbNewLine & ex.Message, True) - End Try - - Try - If IntAppProcess.HasExited = False Then - IntAppProcess.Dispose() - End If - Catch ex As Exception - MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg, My.Language.strIntAppDisposeFailed & vbNewLine & ex.Message, True) - End Try - - MyBase.Close() - End Sub -#End Region - -#Region "Public Shared Methods" - -#End Region - -#Region "Enums" - Public Enum Defaults - Port = 0 - End Enum -#End Region - - End Class - End Namespace -End Namespace diff --git a/mRemoteV1/Connection/Connection.Protocol.IntegratedProgram.vb b/mRemoteV1/Connection/Connection.Protocol.IntegratedProgram.vb new file mode 100644 index 00000000..113df5ad --- /dev/null +++ b/mRemoteV1/Connection/Connection.Protocol.IntegratedProgram.vb @@ -0,0 +1,121 @@ +Imports mRemoteNG.App.Native +Imports System.Threading +Imports mRemoteNG.App.Runtime +Imports mRemoteNG.Tools + +Namespace Connection + Namespace Protocol + Public Class IntegratedProgram + Inherits Base +#Region "Public Methods" + Public Overrides Function SetProps() As Boolean + If InterfaceControl.Info IsNot Nothing Then + _externalTool = GetExtAppByName(InterfaceControl.Info.ExtApp) + _externalTool.ConnectionInfo = InterfaceControl.Info + End If + + Return MyBase.SetProps() + End Function + + Public Overrides Function Connect() As Boolean + Try + If _externalTool.TryIntegrate = False Then + _externalTool.Start(InterfaceControl.Info) + Close() + Return Nothing + End If + + _process = New Process() + + With _process.StartInfo + .UseShellExecute = True + .FileName = _externalTool.FileName + .Arguments = _externalTool.ParseArguments(_externalTool.Arguments) + End With + + _process.EnableRaisingEvents = True + AddHandler _process.Exited, AddressOf ProcessExited + + _process.Start() + _process.WaitForInputIdle(My.Settings.MaxPuttyWaitTime * 1000) + + Dim startTicks As Integer = Environment.TickCount + While _handle.ToInt32 = 0 And Environment.TickCount < startTicks + (My.Settings.MaxPuttyWaitTime * 1000) + _process.Refresh() + If Not _process.MainWindowTitle = "Default IME" Then _handle = _process.MainWindowHandle + If _handle.ToInt32 = 0 Then Thread.Sleep(0) + End While + + SetParent(_handle, InterfaceControl.Handle) + + MessageCollector.AddMessage(Messages.MessageClass.InformationMsg, My.Language.strIntAppStuff, True) + + MessageCollector.AddMessage(Messages.MessageClass.InformationMsg, String.Format(My.Language.strIntAppHandle, _handle.ToString), True) + MessageCollector.AddMessage(Messages.MessageClass.InformationMsg, String.Format(My.Language.strIntAppTitle, _process.MainWindowTitle), True) + MessageCollector.AddMessage(Messages.MessageClass.InformationMsg, String.Format(My.Language.strIntAppParentHandle, InterfaceControl.Parent.Handle.ToString), True) + + Resize(Me, New EventArgs) + + MyBase.Connect() + Return True + Catch ex As Exception + MessageCollector.AddExceptionMessage(My.Language.strIntAppConnectionFailed, ex) + Return False + End Try + End Function + + Public Overrides Sub Focus() + Try + If ConnectionWindow.InTabDrag Then Return + SetForegroundWindow(_handle) + Catch ex As Exception + MessageCollector.AddExceptionMessage(My.Language.strIntAppFocusFailed, ex, , True) + End Try + End Sub + + Public Overrides Sub Resize(ByVal sender As Object, ByVal e As EventArgs) + Try + If InterfaceControl.Size = Size.Empty Then Return + MoveWindow(_handle, -SystemInformation.FrameBorderSize.Width, -(SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height), InterfaceControl.Width + (SystemInformation.FrameBorderSize.Width * 2), InterfaceControl.Height + SystemInformation.CaptionHeight + (SystemInformation.FrameBorderSize.Height * 2), True) + Catch ex As Exception + MessageCollector.AddExceptionMessage(My.Language.strIntAppResizeFailed, ex, , True) + End Try + End Sub + + Public Overrides Sub Close() + Try + If Not _process.HasExited Then _process.Kill() + Catch ex As Exception + MessageCollector.AddExceptionMessage(My.Language.strIntAppKillFailed, ex, , True) + End Try + + Try + If Not _process.HasExited Then _process.Dispose() + Catch ex As Exception + MessageCollector.AddExceptionMessage(My.Language.strIntAppDisposeFailed, ex, , True) + End Try + + MyBase.Close() + End Sub +#End Region + +#Region "Private Fields" + Private _externalTool As ExternalTool + Private _handle As IntPtr + Private _process As Process +#End Region + +#Region "Private Methods" + Private Sub ProcessExited(ByVal sender As Object, ByVal e As EventArgs) + Event_Closed(Me) + End Sub +#End Region + +#Region "Enumerations" + Public Enum Defaults + Port = 0 + End Enum +#End Region + End Class + End Namespace +End Namespace diff --git a/mRemoteV1/Help/ExternalApps.htm b/mRemoteV1/Help/ExternalApps.htm deleted file mode 100644 index 728ee88c..00000000 --- a/mRemoteV1/Help/ExternalApps.htm +++ /dev/null @@ -1,113 +0,0 @@ - - - - External Applications - - - - -

- Introduction to External Applications

-

- External applications can help you get things done that can't be done in - mRemote.
- You can for example start a CMD Console or launch your favorite FTP tool from - within mRemote.
- This wouldn't make much sense by itself because you can already launch your - applications by using the Windows Start Menu, Quick Launch or whatever you - prefer to use - to start your apps.

-

- But there's more!

-

- In mRemote you can launch applications and tell them what to do with the use of - arguments (parameters) and variables of the currently selected Connection.
- You can for example select your home router's SSH Connection entry and do a - traceroute (tracert) on that host.

-

- This is much more comfortable and powerful than opening the console and typing - tracert YourHost.

-

- Notes:

-
    -
  • Throught this document I will refer to External Applications as Ext. apps or - Ext. app.
  • -
  • Ext. apps are stored in c:\documents and settings\username\local - settings\application data\Felix_Deimel\mRemote\extApps.xml
  • -
-

- Variables

-

- Variables and arguments can be used to tell the ext. app what to do.
- Here's a list of variables currently supported by mRemote:

-
    -
  • %Name%
  • -
  • %Hostname%
  • -
  • %Port%
  • -
  • %Username%
  • -
  • %Password%
  • -
  • %Domain%
  • -
  • %Description%
  • -
  • %MacAddress%
  • -
  • %UserField%
  • -
-

- The variables always refer to the currently selected Connection.

-

- Example

-

- First of all start the Ext. Apps management interface. To do this click Tools in the main menu and - select External Applications.
- You will see a screen like on the following screenshot.

-

-

-

- The fields below the list are greyed out because you haven't created a Ext. App - entry yet.
- To create one right click the blank area in the list and select Add like in the - screenshot below.

-

-

-

- This is what you'll get:

-

-

-

- So the three fields are now available and need to be filled.
- The Display Name is simply the name you will see when you want to launch that - application, so give it a descriptive name.
- I named mine Traceroute as I will create a Ext. App launcher that will do a - tracert command in the console.

-

-

-

- Ok, the next thing we'll need is a filename. This is the application that we - want to be executed.
- I simply type in cmd for a windows cmd console.

-

-

-

- Now the fun part comes in - the arguments.
- The windows cmd has a command line argument that tells the console to launch the - command followed by that argument and stay open.
- It's /K. (There's also /C, this is useful when you want the console to close - after the command was executed)
- In this case I'll use /K as I want to scan through the result when the command - completes.
- After that I just type tracert %HostName%. This tells the console to do a - traceroute on the hostname of the currently selected Connection.

-

-

-

- Alright! That's all we'll need.
- Now right click one of you connections, click Tools, External Applications - and select Traceroute.

-

-

-

- Voilà! A console window will popup and execute your tracert command.

-

-

- - - \ No newline at end of file diff --git a/mRemoteV1/Help/ExternalTools.htm b/mRemoteV1/Help/ExternalTools.htm new file mode 100644 index 00000000..5f9a91af --- /dev/null +++ b/mRemoteV1/Help/ExternalTools.htm @@ -0,0 +1,245 @@ + + + + External Tools + + + + + +

+ Introduction to External Tools

+

+ External Tools can help you get things done that can't be done in + mRemoteNG.
+ You can for example start a command prompt or launch your favorite FTP tool from + within mRemoteNG.
+ This wouldn't make much sense by itself because you can already launch your + applications by using the Windows Start Menu, Quick Launch or whatever you + prefer to use + to start your apps.

+

+ But there's more!

+

+ In mRemoteNG, you can launch applications and tell them what to do with the use of + arguments (parameters) and variables of the currently selected Connection. + You can, for example, select your home router's SSH Connection entry and do a + traceroute (tracert) on that host. + This is much quicker and more powerful than opening the console and typing + "tracert yourhost".

+

+ The external tools configuration is stored in %APPDATA%\mRemoteNG\extApps.xml

+ +

+ Variables +

+ +

+ Variables and arguments can be used to tell the external tool what to do.

+

+ This is the list of variables supported by mRemoteNG: +

+ +
    +
  • %NAME%
  • +
  • %HOSTNAME%
  • +
  • %PORT%
  • +
  • %USERNAME%
  • +
  • %PASSWORD%
  • +
  • %DOMAIN%
  • +
  • %DESCRIPTION%
  • +
  • %MACADDRESS%
  • +
  • %USERFIELD%
  • +
+ +

+ Variables always refer to the currently selected connection. Variable names are case-insensitive. + Variables can be used in both the Filename and Arguments fields.

+ +

+ mRemoteNG will also expand environment variables such as %PATH% and + %USERPROFILE%. + If you need to use an environment variable with the same name as an mRemoteNG + variable, use \% instead of %. The most common use of this is for the USERNAME + environment variable. %USERNAME% will be expanded to the username set in the + currently selected connection. \%USERNAME\% will be expanded to the value set in + the USERNAME environment variable.

+ +

If you need to send a variable name to a program without mRemoteNG expanding it, + use ^% instead of %. mRemoteNG will remove the caret (^) and leave the rest + unchanged. For example, ^%USERNAME^% will be sent to the program as %USERNAME% + and will not be expanded.

+ +

+ Special Character Escaping +

+ +

+ Expanded variables will be escaped using the rules below. There are two levels + of escaping that are done. The first is escaping for standard argument splitting + (C/C++ argv, CommandLineToArgvW, etc). The second is escaping shell + metacharacters for ShellExecute.

+

+ Argument splitting escaping:

+ +
    +
  • Each quotation mark will be escaped by a backslash.
  • +
  • One or more backslashes (\) followed by a quotation mark ("): +
      +
    • Each backslash will be escaped by another backslash.
    • +
    • The quotation mark will be escaped by a backslash.
    • +      If the connection's user field contains + "This" is a \"test\".
      +      Then %USERFIELD% is replaced with + \"This\" is a \\\"test\\\". +
    +
  • +
  • A variable name followed by a quotation mark (for example, %USERFIELD%") with + a value ending in one or more backslashes: +
      +
    • Each backslash will be escaped by another backslash.
    • +
    • Example:
    • +      If the connection's user field contains c:\Example\
      +      Then "%USERFIELD%" is replaced with "c:\Example\\" +
    +
  • +
+ +

+ To disable + argument splitting escaping for a variable, precede its name with a minus (-) + sign. For example, %-USERFIELD%.

+

+ Shell metacharacter escaping:

+ +
    +
  • The shell metacharacters are ( ) % ! ^ " < > & |
  • +
  • Each shell metacharacter will be escaped by a caret (^).
  • +
+

+ To disable both argument splitting and shell metacharacter escaping for a + variable, precede its name with an exclamation point (!). For example, + %!USERFIELD%. This is not recommended and may cause unexpected results.

+

+ Only variables that have been expanded will be escaped. It is up to you to + escape the rest of the arguments.

+ +

+ Variable Examples

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArgumentsUser FieldResult
%USERFIELD%"Example" Text + \^"Example\^" Text
%-USERFIELD%"Example" Text + ^"Example^" Text
%!USERFIELD%"Example" Text + "Example" Text
^%USERFIELD^%"Example" Text%USERFIELD%
^^%USERFIELD^^%"Example" Text + ^%USERFIELD^%
-d "%USERFIELD%"c:\Example\-d "c:\Example\\"
-d "%-USERFIELD%"c:\Example\-d "c:\Example\"
-d "%USERFIELD%"Left + & Right-d "Left ^& Right"
-d "%!USERFIELD%"Left + & Right-d "Left & Right"
%WINDIR%N/Ac:\Windows\
\%WINDIR\%N/Ac:\Windows\
\^%WINDIR\^%N/A + \%WINDIR\%
\\%WINDIR\\%N/A\\%WINDIR\\%
+ +

+ Example

+

+ First of all, start the external tools editor. To do this, click Tools in the main menu and + select External Tools.
+ You will see a screen like on the following screenshot.

+

+

+

+ The fields below the list are greyed out because you haven't created an external tool + entry yet.
+ To create one, right click the blank area in the list and select Add, as in the + screenshot below.

+

+

+

+ This is what you'll get:

+

+

+

+ So the three fields are now available and need to be filled.
+ The Display Name is simply the name you will see when you want to launch that + tool, so give it a descriptive name.
+ I named mine Traceroute as I will create a external tool that will start the + tracert command in the console.

+

+

+

+ Ok, the next thing we'll need is a filename. This is the program that we + want to be executed.
+ I simply type in cmd for a Windows cmd console.

+

+

+

+ Now the fun part comes in—the arguments.
+ The Windows cmd has a command line argument that tells the console to launch the + command followed by that argument and stay open.
+ It's /K. (There's also /C, this is useful when you want the console to close + after the command was executed)
+ In this case, I'll use /K as I want to look through the result when the command + completes.
+ After that, I just type tracert %HostName%. This tells the console to do a + traceroute on the hostname of the currently selected Connection.

+

+

+

+ Alright! That's all we'll need.
+ Now right click one of you connections, click Tools, External Tools + and select Traceroute.

+

+

+

+ Voilà! A console window will popup and execute your tracert command.

+

+

+ + + \ No newline at end of file diff --git a/mRemoteV1/Help/Index.htm b/mRemoteV1/Help/Index.htm index 2ad9c607..38846561 100644 --- a/mRemoteV1/Help/Index.htm +++ b/mRemoteV1/Help/Index.htm @@ -30,7 +30,7 @@
  • SSH File Transfer
  • Quick Connect
  • Import from Active Directory
  • -
  • External Applications
  • +
  • External Tools
  • Port Scan
  • Quick Reference

    diff --git a/mRemoteV1/Help/Main.css b/mRemoteV1/Help/Main.css index 50a8cade..b2b8afd1 100644 --- a/mRemoteV1/Help/Main.css +++ b/mRemoteV1/Help/Main.css @@ -107,6 +107,25 @@ a:hover +/* TABLES */ + +table { + border-collapse: collapse; +} + +table, th, td { + border: 1px solid black; +} + +th,td { + padding: 5px; +} + +th { + background-color: lightgrey; + text-align: left; +} + /* MISC STYLES */ @@ -117,4 +136,13 @@ a:hover color: #000000; font-family: 'Courier New' , Monospace; background-color: #C1C1C1; -} \ No newline at end of file +} + +.monospace { + white-space: nowrap; + font-family: 'Courier New', monospace; +} + +.nowrap { + white-space: nowrap +} diff --git a/mRemoteV1/Help/Screenshots/External Apps/01.png b/mRemoteV1/Help/Screenshots/External Tools/01.png similarity index 100% rename from mRemoteV1/Help/Screenshots/External Apps/01.png rename to mRemoteV1/Help/Screenshots/External Tools/01.png diff --git a/mRemoteV1/Help/Screenshots/External Apps/02.png b/mRemoteV1/Help/Screenshots/External Tools/02.png similarity index 100% rename from mRemoteV1/Help/Screenshots/External Apps/02.png rename to mRemoteV1/Help/Screenshots/External Tools/02.png diff --git a/mRemoteV1/Help/Screenshots/External Apps/03.png b/mRemoteV1/Help/Screenshots/External Tools/03.png similarity index 100% rename from mRemoteV1/Help/Screenshots/External Apps/03.png rename to mRemoteV1/Help/Screenshots/External Tools/03.png diff --git a/mRemoteV1/Help/Screenshots/External Apps/04.png b/mRemoteV1/Help/Screenshots/External Tools/04.png similarity index 100% rename from mRemoteV1/Help/Screenshots/External Apps/04.png rename to mRemoteV1/Help/Screenshots/External Tools/04.png diff --git a/mRemoteV1/Help/Screenshots/External Apps/05.png b/mRemoteV1/Help/Screenshots/External Tools/05.png similarity index 100% rename from mRemoteV1/Help/Screenshots/External Apps/05.png rename to mRemoteV1/Help/Screenshots/External Tools/05.png diff --git a/mRemoteV1/Help/Screenshots/External Apps/06.png b/mRemoteV1/Help/Screenshots/External Tools/06.png similarity index 100% rename from mRemoteV1/Help/Screenshots/External Apps/06.png rename to mRemoteV1/Help/Screenshots/External Tools/06.png diff --git a/mRemoteV1/Help/Screenshots/External Apps/07.png b/mRemoteV1/Help/Screenshots/External Tools/07.png similarity index 100% rename from mRemoteV1/Help/Screenshots/External Apps/07.png rename to mRemoteV1/Help/Screenshots/External Tools/07.png diff --git a/mRemoteV1/Help/Screenshots/External Apps/08.png b/mRemoteV1/Help/Screenshots/External Tools/08.png similarity index 100% rename from mRemoteV1/Help/Screenshots/External Apps/08.png rename to mRemoteV1/Help/Screenshots/External Tools/08.png diff --git a/mRemoteV1/Tools/CommandLineArguments.vb b/mRemoteV1/Tools/CommandLineArguments.vb index a5328268..c000cb32 100644 --- a/mRemoteV1/Tools/CommandLineArguments.vb +++ b/mRemoteV1/Tools/CommandLineArguments.vb @@ -52,17 +52,19 @@ Namespace Tools Return Regex.Replace(argument, "(\\*)""", "$1$1\""") End Function - Public Shared Function QuoteArgument(ByVal argument As String, Optional ByVal forceQuotes As Boolean = False) As String - If Not forceQuotes And Not String.IsNullOrEmpty(argument) And Not argument.Contains(" ") Then Return argument + Public Shared Function EscapeBackslashesForTrailingQuote(ByVal argument As String) As String + If String.IsNullOrEmpty(argument) Then Return argument ' Sequence of backslashes followed by the end of the string ' (which will become a double quote): ' double up all the backslashes - If Not String.IsNullOrEmpty(argument) Then - argument = Regex.Replace(argument, "(\\*)$", "$1$1") - End If + Return Regex.Replace(argument, "(\\*)$", "$1$1") + End Function - Return """" & argument & """" + Public Shared Function QuoteArgument(ByVal argument As String, Optional ByVal forceQuotes As Boolean = False) As String + If Not forceQuotes And Not String.IsNullOrEmpty(argument) And Not argument.Contains(" ") Then Return argument + + Return """" & EscapeBackslashesForTrailingQuote(argument) & """" End Function Public Shared Function EscapeShellMetacharacters(ByVal argument As String) As String diff --git a/mRemoteV1/Tools/ExternalTool.vb b/mRemoteV1/Tools/ExternalTool.vb new file mode 100644 index 00000000..ec0b5bf8 --- /dev/null +++ b/mRemoteV1/Tools/ExternalTool.vb @@ -0,0 +1,297 @@ +Imports mRemoteNG.App.Runtime +Imports System.IO +Imports System.ComponentModel + +Namespace Tools + Public Class ExternalTool +#Region "Public Properties" + Public Property DisplayName() As String + Public Property FileName() As String + Public Property WaitForExit() As Boolean + Public Property Arguments() As String + Public Property TryIntegrate() As Boolean + Public Property ConnectionInfo() As Connection.Info + + Public ReadOnly Property Icon() As Icon + Get + If File.Exists(FileName) Then + Return Misc.GetIconFromFile(FileName) + Else + Return Nothing + End If + End Get + End Property + + Public ReadOnly Property Image() As Image + Get + If Icon IsNot Nothing Then + Return Icon.ToBitmap + Else + Return Nothing + End If + End Get + End Property +#End Region + +#Region "Constructors" + Public Sub New(Optional ByVal displayName As String = "", Optional ByVal fileName As String = "", Optional ByVal arguments As String = "") + Me.DisplayName = displayName + Me.FileName = fileName + Me.Arguments = arguments + End Sub +#End Region + +#Region "Public Methods" + ' Start external app + Public Sub Start(Optional ByVal startConnectionInfo As Connection.Info = Nothing) + Try + If String.IsNullOrEmpty(_FileName) Then Throw New InvalidOperationException("FileName cannot be blank.") + + If TryIntegrate Then + StartIntegrated(startConnectionInfo) + Return + End If + + ConnectionInfo = startConnectionInfo + + Dim process As New Process() + With process.StartInfo + .UseShellExecute = True + .FileName = FileName + .Arguments = ParseArguments(Arguments) + End With + + process.Start() + + If WaitForExit Then process.WaitForExit() + Catch ex As Exception + MessageCollector.AddExceptionMessage("ExternalApp.Start() failed.", ex) + End Try + End Sub + + ' Start external app integrated + Public Sub StartIntegrated(Optional ByVal startConnectionInfo As Connection.Info = Nothing) + Try + ConnectionInfo = startConnectionInfo + + Dim newConnectionInfo As New Connection.Info + + newConnectionInfo.Protocol = Connection.Protocol.Protocols.IntApp + newConnectionInfo.ExtApp = DisplayName + newConnectionInfo.Name = DisplayName + newConnectionInfo.Panel = "Int. Apps" + newConnectionInfo.Hostname = ConnectionInfo.Hostname + newConnectionInfo.Port = ConnectionInfo.Port + newConnectionInfo.Username = ConnectionInfo.Username + newConnectionInfo.Password = ConnectionInfo.Password + newConnectionInfo.Domain = ConnectionInfo.Domain + newConnectionInfo.Description = ConnectionInfo.Description + newConnectionInfo.MacAddress = ConnectionInfo.MacAddress + newConnectionInfo.UserField = ConnectionInfo.UserField + newConnectionInfo.Description = ConnectionInfo.Description + newConnectionInfo.PreExtApp = ConnectionInfo.PreExtApp + newConnectionInfo.PostExtApp = ConnectionInfo.PostExtApp + + OpenConnection(newConnectionInfo) + Catch ex As Exception + MessageCollector.AddExceptionMessage("ExternalApp.StartIntegrated() failed.", ex, , True) + End Try + End Sub + + Private Enum EscapeType + All + ShellMetacharacters + None + End Enum + + Private Structure Replacement + Public Sub New(ByVal start As Integer, ByVal length As Integer, ByVal value As String) + Me.Start = start + Me.Length = length + Me.Value = value + End Sub + + Public Property Start As Integer + Public Property Length As Integer + Public Property Value As String + End Structure + + Public Function ParseArguments(ByVal input As String) As String + Dim index As Integer = 0 + Dim replacements As New List(Of Replacement) + + Do + Dim tokenStart As Integer = input.IndexOf("%", index, StringComparison.InvariantCulture) + If tokenStart = -1 Then Exit Do + + Dim tokenEnd As Integer = input.IndexOf("%", tokenStart + 1, StringComparison.InvariantCulture) + If tokenEnd = -1 Then Exit Do + + Dim tokenLength As Integer = tokenEnd - tokenStart + 1 + + Dim variableNameStart As Integer = tokenStart + 1 + Dim variableNameLength As Integer = tokenLength - 2 + + Dim isEnvironmentVariable As Boolean = False + + Dim variableName As String + + If tokenStart > 0 Then + Dim tokenStartPrefix As Char = input.Substring(tokenStart - 1, 1) + Dim tokenEndPrefix As Char = input.Substring(tokenEnd - 1, 1) + + If tokenStartPrefix = "\" And tokenEndPrefix = "\" Then + isEnvironmentVariable = True + + ' Add the first backslash to the token + tokenStart = tokenStart - 1 + tokenLength = tokenLength + 1 + + ' Remove the last backslash from the name + variableNameLength = variableNameLength - 1 + ElseIf tokenStartPrefix = "^" And tokenEndPrefix = "^" Then + ' Add the first caret to the token + tokenStart = tokenStart - 1 + tokenLength = tokenLength + 1 + + ' Remove the last caret from the name + variableNameLength = variableNameLength - 1 + + variableName = input.Substring(variableNameStart, variableNameLength) + replacements.Add(New Replacement(tokenStart, tokenLength, String.Format("%{0}%", variableName))) + + index = tokenEnd + Continue Do + End If + End If + + Dim token As String = input.Substring(tokenStart, tokenLength) + + Dim escape As EscapeType = EscapeType.All + Dim prefix As String = input.Substring(variableNameStart, 1) + Select Case prefix + Case "-" + escape = EscapeType.ShellMetacharacters + Case "!" + escape = EscapeType.None + End Select + + If Not escape = EscapeType.All Then + ' Remove the escape character from the name + variableNameStart = variableNameStart + 1 + variableNameLength = variableNameLength - 1 + End If + + If variableNameLength = 0 Then + index = tokenEnd + Continue Do + End If + + variableName = input.Substring(variableNameStart, variableNameLength) + + Dim replacementValue As String = token + If Not isEnvironmentVariable Then + replacementValue = GetVariableReplacement(variableName, token) + End If + + Dim haveReplacement As Boolean = False + + If Not replacementValue = token Then + haveReplacement = True + Else + replacementValue = Environment.GetEnvironmentVariable(variableName) + If replacementValue IsNot Nothing Then haveReplacement = True + End If + + If haveReplacement Then + Dim trailing As Char + If tokenEnd + 2 <= input.Length Then + trailing = input.Substring(tokenEnd + 1, 1) + Else + trailing = String.Empty + End If + + If escape = EscapeType.All Then + replacementValue = CommandLineArguments.EscapeBackslashes(replacementValue) + If trailing = """" Then + replacementValue = CommandLineArguments.EscapeBackslashesForTrailingQuote(replacementValue) + End If + End If + + If escape = EscapeType.All Or escape = EscapeType.ShellMetacharacters Then + replacementValue = CommandLineArguments.EscapeShellMetacharacters(replacementValue) + End If + + replacements.Add(New Replacement(tokenStart, tokenLength, replacementValue)) + index = tokenEnd + 1 + Else + index = tokenEnd + End If + Loop + + Dim result As String = input + + For index = result.Length To 0 Step -1 + For Each replacement As Replacement In replacements + If Not replacement.Start = index Then Continue For + + Dim before As String = result.Substring(0, replacement.Start) + Dim after As String = result.Substring(replacement.Start + replacement.Length) + result = before & replacement.Value & after + Next + Next + + Return result + End Function +#End Region + +#Region "Private Methods" + Private Function GetVariableReplacement(ByVal variable As String, ByVal original As String) As String + Dim replacement As String + Select Case variable.ToLowerInvariant() + Case "name" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.Name + Case "hostname" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.Hostname + Case "port" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.Port + Case "username" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.Username + Case "password" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.Password + Case "domain" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.Domain + Case "description" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.Description + ' ReSharper disable once StringLiteralTypo + Case "macaddress" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.MacAddress + ' ReSharper disable once StringLiteralTypo + Case "userfield" + If ConnectionInfo Is Nothing Then replacement = "" Else replacement = ConnectionInfo.UserField + Case Else + Return original + End Select + Return replacement + End Function +#End Region + End Class + + Public Class ExternalToolsTypeConverter + Inherits StringConverter + + Public Shared ExternalTools As String() = New String() {} + + Public Overloads Overrides Function GetStandardValues(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.ComponentModel.TypeConverter.StandardValuesCollection + Return New StandardValuesCollection(ExternalTools) + End Function + + Public Overloads Overrides Function GetStandardValuesExclusive(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean + Return True + End Function + + Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean + Return True + End Function + End Class +End Namespace \ No newline at end of file diff --git a/mRemoteV1/Tools/Tools.ExternalApp.vb b/mRemoteV1/Tools/Tools.ExternalApp.vb deleted file mode 100644 index 45536eac..00000000 --- a/mRemoteV1/Tools/Tools.ExternalApp.vb +++ /dev/null @@ -1,220 +0,0 @@ -Imports mRemoteNG.App.Runtime -Imports System.IO -Imports System.ComponentModel - -Namespace Tools - Public Class ExternalTool -#Region "Properties" - Private _DisplayName As String - Public Property DisplayName() As String - Get - Return _DisplayName - End Get - Set(ByVal value As String) - _DisplayName = value - End Set - End Property - - Private _FileName As String - Public Property FileName() As String - Get - Return _FileName - End Get - Set(ByVal value As String) - _FileName = value - End Set - End Property - - Private _WaitForExit As Boolean - Public Property WaitForExit() As Boolean - Get - Return _WaitForExit - End Get - Set(ByVal value As Boolean) - _WaitForExit = value - End Set - End Property - - Private _Arguments As String - Public Property Arguments() As String - Get - Return _Arguments - End Get - Set(ByVal value As String) - _Arguments = value - End Set - End Property - - Private _TryIntegrate As Boolean - Public Property TryIntegrate() As Boolean - Get - Return _TryIntegrate - End Get - Set(ByVal value As Boolean) - _TryIntegrate = value - End Set - End Property - - - - Private _ConnectionInfo As Connection.Info - Public Property ConnectionInfo() As Connection.Info - Get - Return _ConnectionInfo - End Get - Set(ByVal value As Connection.Info) - _ConnectionInfo = value - End Set - End Property - - Public ReadOnly Property Icon() As Icon - Get - If File.Exists(Me._FileName) Then - Return Tools.Misc.GetIconFromFile(Me._FileName) - Else - Return Nothing - End If - End Get - End Property - - Public ReadOnly Property Image() As Image - Get - Dim iC As Icon = Me.Icon - If iC IsNot Nothing Then - Return iC.ToBitmap - Else - Return Nothing - End If - End Get - End Property -#End Region - - Public Sub New() - Me.New("") - End Sub - - Public Sub New(ByVal DisplayName As String) - Me.New(DisplayName, "", "") - End Sub - - Public Sub New(ByVal DisplayName As String, ByVal Filename As String, ByVal Arguments As String) - _DisplayName = DisplayName - _FileName = Filename - _Arguments = Arguments - End Sub - - ' Start external app - Public Function Start(Optional ByVal ConnectionInfo As Connection.Info = Nothing) As Process - Try - If _FileName = "" Then - Throw New Exception("No Filename specified!") - End If - - If _TryIntegrate = True Then - StartIntApp(ConnectionInfo) - Return Nothing - End If - - _ConnectionInfo = ConnectionInfo - - Dim process As New Process() - With process.StartInfo - .UseShellExecute = True - .FileName = ParseText(_FileName) - - .Arguments = ParseText(_Arguments) - End With - - process.Start() - - If _WaitForExit Then - process.WaitForExit() - End If - - Return process - Catch ex As Exception - MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg, "Couldn't start external application." & vbNewLine & ex.Message) - Return Nothing - End Try - End Function - - ' Start external app integrated - Public Sub StartIntApp(Optional ByVal ConnectionInfo As Connection.Info = Nothing) - Try - _ConnectionInfo = ConnectionInfo - - Dim nCI As New Connection.Info - - nCI.Protocol = Connection.Protocol.Protocols.IntApp - nCI.ExtApp = Me.DisplayName - nCI.Name = Me.DisplayName - nCI.Panel = "Int. Apps" - nCI.Hostname = _ConnectionInfo.Hostname - nCI.Port = _ConnectionInfo.Port - nCI.Username = _ConnectionInfo.Username - nCI.Password = _ConnectionInfo.Password - nCI.Domain = _ConnectionInfo.Domain - nCI.Description = _ConnectionInfo.Description - nCI.MacAddress = _ConnectionInfo.MacAddress - nCI.UserField = _ConnectionInfo.UserField - nCI.Description = _ConnectionInfo.Description - nCI.PreExtApp = _ConnectionInfo.PreExtApp - nCI.PostExtApp = _ConnectionInfo.PostExtApp - - OpenConnection(nCI) - Catch ex As Exception - - End Try - End Sub - - Public Function ParseText(ByVal Text As String) As String - Dim pText As String = Text - - Try - If _ConnectionInfo IsNot Nothing Then - pText = Replace(pText, "%Name%", _ConnectionInfo.Name, , , CompareMethod.Text) - pText = Replace(pText, "%HostName%", _ConnectionInfo.Hostname, , , CompareMethod.Text) - pText = Replace(pText, "%Port%", _ConnectionInfo.Port, , , CompareMethod.Text) - pText = Replace(pText, "%UserName%", _ConnectionInfo.Username, , , CompareMethod.Text) - pText = Replace(pText, "%Password%", _ConnectionInfo.Password, , , CompareMethod.Text) - pText = Replace(pText, "%Domain%", _ConnectionInfo.Domain, , , CompareMethod.Text) - pText = Replace(pText, "%Description%", _ConnectionInfo.Description, , , CompareMethod.Text) - pText = Replace(pText, "%MacAddress%", _ConnectionInfo.MacAddress, , , CompareMethod.Text) - pText = Replace(pText, "%UserField%", _ConnectionInfo.UserField, , , CompareMethod.Text) - Else - pText = Replace(pText, "%Name%", "", , , CompareMethod.Text) - pText = Replace(pText, "%HostName%", "", , , CompareMethod.Text) - pText = Replace(pText, "%Port%", "", , , CompareMethod.Text) - pText = Replace(pText, "%UserName%", "", , , CompareMethod.Text) - pText = Replace(pText, "%Password%", "", , , CompareMethod.Text) - pText = Replace(pText, "%Domain%", "", , , CompareMethod.Text) - pText = Replace(pText, "%Description%", "", , , CompareMethod.Text) - pText = Replace(pText, "%MacAddress%", "", , , CompareMethod.Text) - pText = Replace(pText, "%UserField%", "", , , CompareMethod.Text) - End If - Catch ex As Exception - MessageCollector.AddMessage(Messages.MessageClass.WarningMsg, "ParseText failed (Tools.ExternalApp)" & vbNewLine & ex.Message, True) - End Try - - Return pText - End Function - End Class - - Public Class ExternalAppsTypeConverter - Inherits StringConverter - - Public Shared ExternalApps As String() = New String() {} - - Public Overloads Overrides Function GetStandardValues(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.ComponentModel.TypeConverter.StandardValuesCollection - Return New StandardValuesCollection(ExternalApps) - End Function - - Public Overloads Overrides Function GetStandardValuesExclusive(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean - Return True - End Function - - Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean - Return True - End Function - End Class -End Namespace \ No newline at end of file diff --git a/mRemoteV1/Tree/Tree.Node.vb b/mRemoteV1/Tree/Tree.Node.vb index 17ab8e79..68ad7477 100644 --- a/mRemoteV1/Tree/Tree.Node.vb +++ b/mRemoteV1/Tree/Tree.Node.vb @@ -368,15 +368,16 @@ Namespace Tree SetNodeImageIndex(treeNode, Img) End Sub - Private Delegate Sub SetNodeImageIndexCB(ByVal tNode As TreeNode, ByVal ImgIndex As Integer) - Private Shared Sub SetNodeImageIndex(ByVal tNode As TreeNode, ByVal ImgIndex As Integer) - If _TreeView.InvokeRequired Then - Dim s As New SetNodeImageIndexCB(AddressOf SetNodeImageIndex) - _TreeView.Invoke(s, New Object() {tNode, ImgIndex}) - Else - tNode.ImageIndex = ImgIndex - tNode.SelectedImageIndex = ImgIndex + Private Delegate Sub SetNodeImageIndexDelegate(ByVal treeNode As TreeNode, ByVal imageIndex As Integer) + Private Shared Sub SetNodeImageIndex(ByVal treeNode As TreeNode, ByVal imageIndex As Integer) + If treeNode Is Nothing OrElse treeNode.TreeView Is Nothing Then Return + If treeNode.TreeView.InvokeRequired Then + treeNode.TreeView.Invoke(New SetNodeImageIndexDelegate(AddressOf SetNodeImageIndex), New Object() {treeNode, imageIndex}) + Return End If + + treeNode.ImageIndex = imageIndex + treeNode.SelectedImageIndex = imageIndex End Sub Public Shared Sub SetNodeToolTip(ByVal e As MouseEventArgs, ByVal tTip As ToolTip) diff --git a/mRemoteV1/UI/UI.Window.Help.vb b/mRemoteV1/UI/UI.Window.Help.vb index 55281cad..6a611920 100644 --- a/mRemoteV1/UI/UI.Window.Help.vb +++ b/mRemoteV1/UI/UI.Window.Help.vb @@ -131,8 +131,8 @@ Namespace UI TreeNode20.Tag = "ImportFromAD" TreeNode20.Text = "Import From Active Directory" TreeNode21.Name = "Node1" - TreeNode21.Tag = "ExternalApps" - TreeNode21.Text = "External Applications" + TreeNode21.Tag = "ExternalTools" + TreeNode21.Text = "External Tools" TreeNode22.Name = "Node0" TreeNode22.Tag = "PortScan" TreeNode22.Text = "Port Scan" @@ -218,9 +218,9 @@ Namespace UI #End Region #Region "Private Methods" - Private Sub Help_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load - Me.tvIndex.Nodes(0).Expand() - Me.tvIndex.SelectedNode = Me.tvIndex.Nodes(0).Nodes(0) + Private Sub Help_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load + tvIndex.Nodes(0).ExpandAll() + tvIndex.SelectedNode = tvIndex.Nodes(0).Nodes(0) End Sub Private Sub Help_Shown(sender As Object, e As EventArgs) Handles Me.Shown diff --git a/mRemoteV1/mRemoteV1.vbproj b/mRemoteV1/mRemoteV1.vbproj index 79baa760..406dfee3 100644 --- a/mRemoteV1/mRemoteV1.vbproj +++ b/mRemoteV1/mRemoteV1.vbproj @@ -286,7 +286,7 @@ - + @@ -361,7 +361,7 @@ - + @@ -636,7 +636,7 @@ PreserveNewest - + PreserveNewest @@ -723,28 +723,28 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest