Introduction
This article covers an application that was written as a “Proof of Concept”, testing the ability to trigger the launch of an application from a web application. The application is also a bit of a practice in Remoting, a subject that I have not explored very much. The application is intended to work within an Intranet environment as it would not work correctly within an Internet environment. In addition to the main subject, it covers creating remoting proxies to different endpoints, within the same application, something that is a new concept in the 2.0 Framework. Please note that this is not a complete application, it’s full of security holes that could be easily abused, which I’ll discuss later. The complete code for this article can be downloaded here.
The Concept
I was tasked with finding a way of launching future desktop applications from within a Web Application. We have several applications planned that require installation as a desktop application, but portions of the application will also be written in ASP.NET. To facilitate the integration between the two applications, we wanted to allow the web application to start the desktop application to accomplish certain tasks. My first response was to say that it couldn’t be done. Of course, that isn’t exactly the best response in a work environment, so I started doing some research to see if it could be accomplished.
I considered several different ways of accomplishing the goal, including registering a file type based on the application to be launched. Unfortunately, I couldn’t figure out any way to get the application to open without requiring the user to click “Open”, or worse yet, save the file somewhere and then open it. Ultimately, I decided the only way to get it to work nicely would be to have an application running on the desktop which would launch the application on request. I had recently used remoting for a personal application and thought it might be a good fit for this application, so I started the design of the remoting class.
The Remoting Class
The remoting class has two main methods, Launch which starts an application, and RegisterApplication which is intended to be used locally upon installation of a new application.
The RegisterApplication (shown below) adds a newly installed application to the list of launchable applications, stored within an XML file (Represented by a typed dataset – Applications). This addresses two problems – the first being that the Web Application has no knowledge of where an application is installed on the remote machine. The second is that it limits the list of applications that can be started remotely, as an attempt to patch some of the security holes associated with this application.
Private SettingsFile As String = My.Application.Info.DirectoryPath + "\Apps.xml"
Public Sub RegisterApplication(ByVal ApplicationName As String, ByVal Location As String)
Trace.WriteLine("Request to register " + ApplicationName + " at " + Location)
Try
Dim dsApps As New Applications
If My.Computer.FileSystem.FileExists(SettingsFile) Then
dsApps.ReadXml(SettingsFile)
End If
Dim drApp As Applications.RegisteredAppsRow = dsApps.RegisteredApps.FindByAppName(ApplicationName)
If drApp Is Nothing Then
drApp = dsApps.RegisteredApps.NewRegisteredAppsRow()
drApp.AppName = ApplicationName
drApp.Location = Location
dsApps.RegisteredApps.Rows.Add(drApp)
Trace.WriteLine("New app - adding")
Else
drApp.Location = Location
Trace.WriteLine("Found app - updating location")
End If
dsApps.WriteXml(SettingsFile)
Catch ex As Exception
Trace.WriteLine(ex.ToString())
End Try
End Sub
The Launch method has two parameters, ApplicationName – the name used to register a specific application, and Parameters – any switches that should be provided when executing the application. The Name parameter allows us to look up the location of the application, and the Parameters provide us a method to feed information to the launched application. This method also relies on a private function, RetrieveAppDirectory. This function reads the XML file and returns the location of the installed application, if present. Once returned, the Launch method simply uses the Process.Start function to start the application. Both methods can be found below:
Public Sub Launch(ByVal ApplicationName As String, ByVal Parameters As String)
Trace.WriteLine("Requested to launch " + ApplicationName + " " + Parameters)
Dim sAppLoc As String = RetrieveAppDirectory(ApplicationName)
If sAppLoc <> "" Then
Try
Process.Start(sAppLoc, Parameters)
Trace.WriteLine("Launched application " + sAppLoc)
Catch ex As Exception
Trace.WriteLine(ex.ToString())
End Try
Else
Trace.WriteLine("Application not registered")
End If
End Sub
Private Function RetrieveAppDirectory(ByVal AppName As String) As String
Dim sAppLoc As String = ""
If My.Computer.FileSystem.FileExists(SettingsFile) Then
Try
Dim dsApps As New Applications
dsApps.ReadXml(SettingsFile)
With dsApps.RegisteredApps
If .Rows.Count > 0 Then
If .FindByAppName(AppName) IsNot Nothing Then
sAppLoc = .FindByAppName(AppName).Location
End If
End If
End With
Catch ex As Exception
Trace.WriteLine(ex.ToString())
End Try
End If
Return sAppLoc
End Function
The Windows Application
As I mentioned in the first section of this article, we need an application running on the Desktop computer which will be responsible for registering the remoting component, making it available to our Web Application.
The design of the application is fairly simple. It is a windows application, containing one window, simply for the purpose of registering the remoting component, as well as showing an icon in the notification area. For the purpose of this article, I’ll include the Load method from the window. The full window is available in the source download which can be found at the end of this article.
Private Sub AppServer_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
Trace.AutoFlush = True
Trace.Listeners.Add(New TextWriterTraceListener(My.Application.Info.DirectoryPath + "\Applaunch.txt"))
RemotingConfiguration.Configure("AppLaunch.config", False)
Trace.WriteLine("AppLaunch registered")
End Sub
The Web Application
All of the code in this article is fairly simple, requiring a few lines of code to accomplish each of our goals. The code within our ASPX page is no exception, requiring 5 lines of code to remotely launch our application. However, it introduces a concept that is new to the 2.0 Framework, the Activator.GetObject method. Typically when connecting to a remoting object, you register information about the object – the type of remoting object, the channel to be used, etc. In the case of this application, we could be launching applications on an unknown number of computers. The Activator.GetObject function allows us to specify multiple endpoints for the remoting object, instead of just 1. Below, you will see that we pass a URI to the function which returns an instance of our remoting object. The page containing this code is simply a test stub, containing textboxes allowing the machine, application and parameters to be specified.
Protected Sub btnLaunch_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnLaunch.Click
Dim uri As String
uri = "http://" + Me.txtHostname.Text.Trim() + ":" + Port + "/AppLaunch"
Dim oLaunch As AppLaunch
oLaunch = CType(Activator.GetObject(GetType(AppLaunch), uri), AppLaunch)
oLaunch.Launch(Me.txtAppToLaunch.Text, Me.txtParameters.Text)
End Sub
The web page also allows registering of an application, something that we wouldn’t use in a production environment, and a security issue, but I’ll cover that later. For now, I’ll use it as a way of showing how to execute the RegisterApplication method. In the next section, I’ll cover considerations on accomplishing registration upon installation.
Protected Sub btnRegister_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnRegister.Click
Dim uri As String
uri = "http://" + Me.txtHostname.Text.Trim() + ":" + Port + "/AppLaunch"
Dim oLaunch As AppLaunch
oLaunch = CType(Activator.GetObject(GetType(AppLaunch), uri), AppLaunch)
oLaunch.RegisterApplication(Me.txtApplication.Text, Me.txtDirectory.Text)
End Sub
Registering an Application
Above, you will see the code needed to register an application. Ideally, this would happen upon installation of an application. Although I have not completed development for this, I would like to provide some guidance for anyone wishing to implement this in their environment.
As part of a setup project, it is possible to specify a Custom Action and provide installation information to the action. A Custom Action is actually a special type of application (a good walkthrough can be found in Visual Studio help – Custom Actions, Creating). This could be used to trigger registration of an application at installation.
Where do we go from here?
As I’ve mentioned several times in this article, there are numerous holes in this application. As-is, it carries considerable risk of being abused. I hope to address these problems in a future article, but for now, I’ll cover my “laundry list” of additions to the application.
- Validate launch requests – possibly require some sort of Key or restrict launch requests to certain IP addresses.
- Encrypt the list of registered applications - This can be accomplished fairly simply with XML Encryption
- Application Registration should be restricted to the local machine only
The complete code for this article can be downloaded here.