Browse DevX
Sign up for e-mail newsletters from DevX


Using the ASP.NET Runtime to Extend Desktop Applications with HTML Scripts : Page 2

People often think of HTML as the sole domain for Web applications. But HTML's versatile display attributes are also very useful for handling data display of all sorts in desktop applications. The Visual Studio .NET start page is a good example. Coupled with a scripting/template mechanism you can build highly extendable applications that would be very difficult to build using standard Windows controls. In this article, Rick introduces how to host the ASP.NET runtime in desktop applications and utilize this technology in a completely client-side application using the Web Browser control.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

How It Works
I've based my wwAspRuntimeHost class on a couple of lower level classes—wwAspRuntimeProxy which acts as the remoting proxy reference for the ASP.NET runtime, and wwWorkerRequest which is a subclass of the SimpleWorkerRequest class that I use to handle passing parameters to script pages. The application talks only to the wwAspRuntimeHost class, which acts as a wrapper around the Proxy class to provide error handling for remoting problems since the proxy is actually a remote object reference. wwAspRuntimeProxy does most of the work, performing the nuts and bolts operation of setting up and calling the ASP.NET runtime. It first creates a new Application Domain for the runtime to be hosted in. Microsoft provides a static method, ApplicationHost.CreateApplicationHost, that provides this functionality. Unfortunately, this behavior is not very flexible and exactly what's required is not very well documented. For this reason and after a fair amount of effort spent searching for a more flexible solution, I decided to create my own AppDomain and load the runtime into it. This allows considerably more configuration concerning where the Runtime finds support files (in the code below in the main application's path) and how I can configure the host as a custom class. Further, it doesn't require copying the application's main assembly that hosts these classes into the virtual directory's BIN directory. Listing 3 shows the code to create an ASP.NET-capable AppDomain. The class methods I describe are all part of the wwAspRuntimeProxy class which you can find in the sample code for this article.

Three methods represent the main management methods of the wwAspRuntimeProxy class. CreateApplicationHost essentially creates a new application domain (think of it as a separate process within a process) and assigns a number of properties to it that the ASP.NET runtime requires. The code in Listing 3 shows the minimal configuration required to set up an AppDomain for executing ASP.NET. Once the AppDomain exists, an instance of the runtime host class called loDomain.CreateInstance() will load. Now the ASP.NET runtime host exists and you can access it over AppDomain boundaries via .NET Remoting. Luckily, several built-in classes help with this process. These three methods are static—you don't need an instance to call them and they don't have access to any of the properties of the class. However, the loHost instance created in CreateApplicationDomain is a full remote proxy instance and you set several properties on it to allow calling applications to keep track of where the environment was loaded via the virtual and physical path. A local application's virtual path is nothing more than a label you'll see on error messages that ASP.NET will generate on script errors. You should put the value in a virtual directory format such as "/" or "/LocalScript." Your physical path should point to a specific directory on your hard disk that ASP.NET uses as the root directory for scripts. You can access scripts there by name or relative path. I like to use a physical path below the application's startup path and call it WebDir, or HTML or Templates. So while working on this project I'll use something like: D:\projects\AspNetHosting\bin\debug\WebDir\.The trailing backslash is important by the way.

You need a class that can host ASP.NET—you must derive it from MarshalByRefObject in order to make it accessible across domains, and your derived class should implement one or more methods that can call an ASP.NET request using the HttpWorkerRequest or SimpleWorkerRequest or subclasses thereof. Listing 4 shows the ProcessRequest method, which takes the name of an ASP.NET page in server relative pathing in the format of "test.aspx" or "subdir\test.aspx." To keep my description of this process simple, I've put both the static loader methods and the ProcessRequest method into the same class. When you call the Start() method it returns a remote instance of the wwAspRuntimeProxy class, on which you can call the ProcessRequest() method. This method is the worker method that performs the pass through calls to the ASP.NET runtime. Listing 4 shows the implementation of this method.

Figure 2: Errors inside of a client scripts bring up the detailed error messages in HTML format. No direct error info is returned to the calling application however.
My wwAspRuntimeProxy class does two things: It creates an output file stream and it uses the SimpleWorker class to create a request that it can pass to the HTTP Runtime. The request is essentially similar to the way that IIS receives request information in a Web Server request, except here we're only passing the absolute minimal information to the ASP.NET processing engine: the name of the page to execute and a Query string along with a TextWriter instance to receive the output generated. You want to pass the new instance of the Request to the HttpRuntime for processing, which, in turn, makes the actual ASP.NET parsing call. It's important to understand that you're executing this code remotely in the created AppDomain that also hosts the ASP.NET runtime, so the call to this entire method (loHost.ProcessRequest()) actually runs over the AppDomain remoting architecture. This has some impact on error management.

Your application will return any errors that occur within the script code itself as ASP.NET error pages just like you would see during Web development. Figure 2 shows an error in the For loop caused by not declaring the enumerating variable. Note that this is the only way you can get error information—no property gets set or error exception triggers on this failure, other than inside of the script code itself. This is both useful and limiting—the debug information is very detailed and easily viewable as HTML, but if your app needs this error info internally there's no way to get it except parsing it out of the HTML content.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date