The Internet is becoming the global network for business. WS-Security, cryptography libraries, and SSL encryptions are making data transfer over the Internet safer. HTTP is commonly used as the "Transport Protocol" to exchange data between organizations. GXA specifications are trying to build a common framework to create seamless communication between distributed systems on multiple platforms.
We can exchange business information as XML representations over Web services. Complex data structures can be bundled into a DataSet to be sent across the wire. The Microsoft .NET Framework will breakdown the data into the "structure" (XML Schema) and "data" (XML). Therefore, any complex data manipulation mechanism (DataSet, DataTable, etc. ...) can be easily understood by the consumer of the Web service. But, can we deploy a DLL using Web services? Can we deploy a unit of binary execution over a Web service?
Isn't this question addressed by the Microsoft Smart Client framework? A Smart Client application will download the DLLs from a Web server on demand. It will copy the DLLs to the client machine to execute locally. Isn't it the same as deploying a DLL over a Web service?
The Difference between a Smart Client Application and DLLs over Web Services
Dynamic link library (DLL) is a unit of execution in binary format. A Smart Client app will download the DLL to the client machine. The client DLL can be refreshed on a timer or manually. At all times the client application works with the local copy. The following image explains the architecture of a Smart Client application.
(Figure 1 : Smart Client Architecture)
If the server copy is updated we need to wait until the timer or manual trigger is invoked to update the client copy. This is not an issue in our Web services deployment scenario. The Web service invocation will always get the up-to-date byte stream of the DLL. The client application will always receive the latest copy of the DLL. This could be described as a "live connection" to the server. The proposed application architecture is described in Figure 2.
(Figure 2 : Proposed Architecture - using a Web services to transfer DLL as a binary stream)
What are the limitations? The obvious drawback is speed. A local copy of a smart client application will be faster than fetching a DLL over a Web service. We are also relaying on the client machine to use Reflection mechanisms to create Windows Forms "on the fly". Therefore, we need to have the .NET Framework installed at the client machine.
Note: Another way to transport a DLL over SOAP is to use Web Services Extensions (WSE 1.0) as DIME attachments. I will not cover this option in this article.
Let's investigate how to implement this functionality using a sample.
Sample Application
The sample application has two views. I am trying to create two presentation views (WinForms) to display different information. The logic is to transfer different binary over the wire for different parameters. The two Web forms are:
Note: I have made the presentation interface simple in order to concentrate on the coding logic. Therefore, the Web form will have very little functionality.
User Form: This Web form is for general users of the system. This is mainly read-only data. Figure 3 displays the user form.
(Figure 3: User Form)
Author Form: This Web form is for administrators of the system . The "Authors" can change access permissions using this view. Figure 4 displays the author form
(Figure 4 : Author Form)
The Web service consumer gets a different binary stream depending on the parameters we provide to the Web service (whether it is "user" or "author" ). This binary stream represents whether the user views a "User Form" or an "Author Form". Let's look at the implementation of the solution.
There are four projects in the VS .NET Studio solution
User Form Project - Windows application project
Author Form Project - Windows application project
Web service to deploy requested module (User or Author) - Web service project
Driver program to run the Web service. - Windows application project
The objective is to deliver different information over Web services on demand. If the we like to consume the "User data" we will transfer the User Form DLL over the Web service. The same logic applies to the Author Form DLL.
User Form Project
First let's look at the User Form. This form is for general users of the system. Here are the steps.
Create a Windows application project.
(Figure 5: Creating User Form Windows Application Project)
Create the user form by dragging and dropping controls from the ToolBox. Here are the elements of the User form
Name
Type
Description
Label_Heading
Label
Label display the heading
Label_Details
Label
Display the user text
I have made the implementation simple to concentrate more on the Web service functionality. The form looks like this.
(Figure 6: User Form design view)
Author Form Project
Let's look at the Author form. Here are the steps.
Create a Windows application project.
Create the user form by dragging and dropping controls from the ToolBox. Here are the elements
Name
Type
Description
Label_Heading
Label
Label display the heading
CheckedListBox_Options
CheckedListBox
List of options to manipulate user access
Here is the design view of the Author Form.
(Figure 7 : Author Form design view)
Web Service
This is the core of the application.
Create a Web service project in VS .NET IDE
Name the project "WSAppDeply".
(Figure 8 : WSAppDeploy Web service project)
Rename "Service1.asmx" file to "WSDeploy.asmx"
Create a Web method called "GetAppModule". This Web method will accept a string describing whether the user requires the "User Form" or the "Author Form". Let's have a look at the code:
<WebMethod()> Public Function GetAppModule(ByVal strUserInfo As String) As DataSet
'
' Return assembly as a string
'
Dim retSet As DataSet = Me.PrepareDataSet()
Dim newRow As DataRow = retSet.Tables(0).NewRow()
Dim inFile As System.IO.FileStream
Dim binaryData() As Byte
Dim strAssemblyName As String
Dim strAssemblyPath As String
Dim strBasePath As String
Dim strError As String
strBasePath = "C:/chris/articles/WS Deployment/WSAppDeploy/bin/"
Try
If (LCase(strUserInfo) = "author") Then
strAssemblyName = "AuthorForm.dll"
newRow("ModuleName") = strAssemblyName
newRow("ClassName") = "AuthorForm.AuthorForm"
newRow("MethodName") = "RunForm"
Else
strAssemblyName = "UserForm.dll"
newRow("ModuleName") = strAssemblyName
newRow("ClassName") = "UserForm.UserForm"
newRow("MethodName") = "RunForm"
End If
First we initialize some variables to store data. (we will have a look at the PrepareDataSet() function later). The Web service needs to know where the DLLs are. Therefore, we need to provide the location of the DLLs. This is stored in the "strBasePath" variable.
Note: Please make sure to copy the UserForm.dll and AuthorForm.dll into this directory. This is not the default location for the project binary executables. This is deliberately done to make the application more flexible. We can have a variable pointing to the default debug directory (of the User Form and Author Form Projects) to make our lifer easier. This creates a dependency to UserForm and AuthorForm projects. This way we can deploy the DLL as soon as it is available from myriad methods. (The DLL could be made outside of the organization and emailed to the developer. The developer will copy the DLL to the correct directory and deploy it to the client machine.)
Then we basically use a selection construct to check the type of input. (whether it is "user" or "author"). We need some vital information to obtain the binary stream.
The name of the assembly (ex: UserForm.dll)
The name of the class (This should follow the format "Namespace.ClassName". ex: UserForm.UserForm)
Which method to run in the selected class. (ex: "RunForm" method)
Let's look at the rest of the code.
' Get path to assembly to send to caller.
'
strAssemblyPath = strBasePath + strAssemblyName
' Open the assembly binary file.
'
inFile = New System.IO.FileStream(strAssemblyPath, _
System.IO.FileMode.Open, _
System.IO.FileAccess.Read)
ReDim binaryData(inFile.Length)
inFile.Read(binaryData, 0, inFile.Length)
inFile.Close()
' Convert the binary input into Base64 UUEncoded output.
'
newRow("AssemblyBytes") = System.Convert.ToBase64String(binaryData, _
0, _
First we read the DLL as an input file stream. Then we basically read the byte stream of the DLL into a "binaryData" array and convert it to a Base 64 String. This string is sent to the Web service consumer. (In a commercial implementation we need to encrypt this data stream. Please refer to the "Possible Extensions" section. )
We will also perform some error handling to exit gracefully. Then we package the information in a DataSet and send it to the Web service consumer. The DataSet is comprised of the assembly name, class name, method name, and the binary stream for the entire assembly.
Catch e As Exception
Dim message As String = e.Message
End Try
' Save the row, and accept the changes on the dataset
'
retSet.Tables(0).Rows.Add(newRow)
retSet.AcceptChanges()
' Return the dataset to caller
'
GetAppModule = retSet
End Function
Let's look at the PrepareDataSet() that prepares the DataSet to store information. This function will create a DataSet that will hold four strings in a DataTable. These string will be the DLL name, class name, method name, and the DLL byte stream as a string.
Private Function PrepareDataSet() As DataSet
Dim newTable As New DataTable()
Dim newSet As New DataSet()
newTable.Columns.Add("ModuleName", Type.GetType("System.String"))
newTable.Columns.Add("ClassName", Type.GetType("System.String"))
newTable.Columns.Add("MethodName", Type.GetType("System.String"))
newTable.Columns.Add("AssemblyBytes", Type.GetType("System.String"))
newSet.Tables.Add(newTable)
Return newSet
End Function
Let's see the Web service in action now. We can make the "WSAppDeploy" the "Startup Application" for the Visual Studio .NET solution by right clicking "WSAppDeploy" and selecting "Set as Startup Project". Then we compile and run the project. Let's invoke the Web service with the "user" string as our input parameter. (We are invoking the Web service to retireve the binary stream of the UserForm application over SOAP). The screen should look similar to Figure 9.
(Figure 9 : WSAppDeploy Web service in action)
When we invoke the GetAppModule Web method. We get the following response. (Figure 10)
(Figure 10 : UserForm binary stream as a Web method response.)
Observe the "AssemblyBytes" tag. This is the binary stream of the UserForm Windows Form. Let's try to focus on building a client/driver program to consume this Web service.
Driver Program
Create a VB Windows application project.
Rename the default form to "DriverProgram.vb".
Add a Web reference to the WSAppDeploy Web service. (Right Click on the Project | Select "Add Web Reference" to our "WSAppDeploy" Web service). Your solution explorer should look like this:
(Figure 11 : Solution Explorer after the adding the Web reference)
Create a form by dragging and dropping controls from the ToolBox. Here are the elements of the driver form:
Name
Type
Description
Cmb_Option
Combo Box
Let the user select which view they choose (User or Author view)
btn_Invoke
Button
Button click to initiate the Web service call
The combo box will let the user select between the "UserForm" or the "AuthorForm". The button click will initiate the Web service call to transfer the correct binary for the selected form. The design view of the driver program is similar to figure 12.
(Figure 12: Driver program design view)
Let's have look at the code for the driver program
Private Sub btn_Invoke_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles btn_Invoke.Click
Dim uiModule As [Assembly]
Dim wsAppModule As New localhost.WSDeploy()
Dim dsAppModuleInfo As DataSet
Dim strUserInfo As String
Try
strUserInfo = cmb_Option.Items.Item(cmb_Option.SelectedIndex)
Catch exp As Exception
MsgBox("Please select a user type")
Exit Sub
End Try
First we try to read the selected option from the combo box. A "try "and "catch" block is there to prevent any invalid input.
Dim strModuleName As String
Dim strClassName As String
Dim strMethodName As String
Try
' Ask Web service for an appropriate assembly
'
dsAppModuleInfo = wsAppModule.GetAppModule(strUserInfo)
' Retrieve names from dataset
'
GetModuleNames(dsAppModuleInfo, strModuleName, strClassName,
strMethodName)
' Turn the stream of bytes returned into an
' assembly
'
Dim assemblyBytes() As Byte
assemblyBytes =
System.Convert.FromBase64String(dsAppModuleInfo.Tables(0).Rows(0)("AssemblyBytes
"))
Then we use the WSDeploy Web service to get a binary stream to create our client-end Windows Form. First the Web services response is read as a DataSet (the variable dsAppModuleInfo) . Then we segment the DataSet to delineate the byte stream, the assembly name, the class name, and the method name of the Web form to be created. Finally the binary stream is converted into "Base64String" and stored in an array of bytes.
' Load the assembly into memory
'
uiModule = System.Reflection.Assembly.Load(assemblyBytes)
' Find the class and method within the assembly
'
Dim typeUIForm As Type
Dim methodUIMethod As MethodInfo
Dim obj As Object
typeUIForm = uiModule.GetType(strClassName)
methodUIMethod = typeUIForm.GetMethod(strMethodName)
' Invoke the method
'
obj = Activator.CreateInstance(typeUIForm)
methodUIMethod.Invoke(obj, Nothing)
Catch exp As Exception
' Error encountered - msgbox it
'
MsgBox("Exception: " & exp.Message)
End Try
End Sub
An assembly is generated after using Reflection on the array of bytes. This is called the uiModule. Now we can extract methods from the assembly and load the Windows Form using "Activator.CreateInstance". The following is the GetModuleNames function that extracts the assembly information from the DataSet passed by the WSDeploy Web service.
Private Sub GetModuleNames(ByVal dsModule As DataSet, ByRef strModuleName As
String, ByRef strClassName As String, ByRef strMethodName As String)
strModuleName = dsModule.Tables(0).Rows(0)("ModuleName")
strClassName = dsModule.Tables(0).Rows(0)("ClassName")
strMethodName = dsModule.Tables(0).Rows(0)("MethodName")
End Sub
That's the code folks! Let's make the driver program the "Startup Project". Now let's compile the application and run it. You should see the following window come up.
(Figure 13: Running the driver program)
Depending what you select, either the UserForm (Figure 3) or the AdminForm (figure 4) will appear. The appropriate binary stream is converted to a Windows Form by using Reflection mechanisms by the client machine.
Possible Extensions
Some of the possible extensions to our code:
Encrypt the DLL byte stream. This will provide additional level of security.
This not just for DLLs. You can use Reflection mechanisms to stream any images or data formats using similar code (i.e. Transfer JPEG images using a Web service), but I strongly recommend you to consider DIME first. (Some DIME references are in the "Further Information" section.)
Conclusion
This is an extension to the standard Web services XML data deliverables. We can extend our .NET XML Web services to transfer binary executables as apposes to simple data formats. The advantage is to have a "live connection" to the server and update your client machine with a stream of binary executable over SOAP.
How To Setup the Sample Code
1. You need to have the .NET Framework installed on your machine. (Download .NET Framework)
2. Download the source code
3. Extract the .zip file to a specified directory
4. Create a virtual directory called "WSAppDeploy" in IIS to point to Web service directory.
5. Set "WSDeployClient" project as the "Startup project"
6. Compile and run. You should see Figure 13.
Chris Peiris currently lectures on Distributed Component Architectures (.NET, J2EE & CORBA) at Monash University, Caulfield, Victoria, Australia. He also works as an independent consultant for .NET and EAI implementations. Recently he has been awarded the title "Microsoft Most Valuable Professional" (MVP) for his contributions to .NET Technologies. He has been designing and developing Microsoft solutions since 1995. His expertise lies in developing scalable, high-performance solutions for financial institutions and media groups. He has written many articles, reviews and columns for various online publications including 15Seconds, Developer Exchange (www.Devx.com) and Wrox Press (www.wrox.com) . He co-authored the book "C# Web Service with .NET Remoting and ASP.NET" by Wrox Press. It was followed by "C# for Java Programmers" by Syngress Press as the primary author. Chris frequently presents at professional developer conferences on Microsoft technologies.
His core skills are C++, Java, .NET, DNA, MTS, Site Server, Data Warehousing, WAP, and SQL Server. Chris has a Bachelor of Computing, Bachelor of Business (Accounting), and Masters of Information Technology. He is currently under taking a PhD on "Web Service Trust Agents". He lives with his family in Civic, Canberra ACT. He can be reached at www.chrispeiris.com.
In the second article of his series on Indigo web services, Chris Peiris explains how to host an Indigo web service and examines the IIS, self hosting, and Windows Activation Service hosting options. He then provides step-by-step instructions and sample code for an IIS-hosted and self-hosted Indigo web service. [Read This Article][Top]
In the first part of his series on Microsoft Indigo, Chris Peiris examines the basics of SOA, explains how Indigo fits into the picture and the problems it solves. He then introduces Indigo's programming model and finishes by building a sample Indigo web service using the Microsoft .Net Framework 2.0. [Read This Article][Top]
Adnan Masood concludes his discussion of Microsoft SQL Server Analysis services and Microsoft SQL Server Reporting services. In the final part, he discusses Reporting Server web services and using custom code in reports. [Read This Article][Top]
This article explains the features of the IE Web service behavior and shows how to asynchronously communicate with an ASP.NET Web service directly from the client.
[Read This Article][Top]
Calvin Luttrell shows how to validate e-mail addresses stored in Excel 2003 and
provides a special function for solving that pesky problem Yahoo! mail servers cause. [Read This Article][Top]
This short article describes a quick and easy way to provide some security to an ASP.NET Web service by modifying its associated documentation file. [Read This Article][Top]
Kerberos authentication is the cornerstone of Windows operating system authentication architecture. Web Services Enhancement 2.0 (WSE 2.0) extends Kerberos support to ASP.NET Web services. Chris Peiris explains the support for this new feature in WSE 2.0. [Read This Article][Top]
Chip Irek examines the architectural issues and component design issues of building a .NET application in a service-oriented architecture. [Read This Article][Top]
Thiru Thangarathinam shows how to use asynchronous Web services, Windows
Service applications, server-based timer components and .NET XML API classes to create high-performance, scalable, and flexible applications. [Read This Article][Top]
Part one showed how to transform XML data into HTML by using an XSL stylesheet from within a .NET application. This part explains how to make use of XSLT Extension objects and invoke a C# class method from an XSL stylesheet. [Read This Article][Top]
Mailing List
Want to receive email when the next article is published? Just Click Here to sign up.