Programmer to ProgrammerTM  
Wrox Press Ltd  
   
  Search ASPToday Living Book ASPToday Living Book
Index Full Text
 
ASPToday Home
 
 
Home HOME
Site Map SITE MAP
Index INDEX
Full-text search SEARCH
Forum FORUM
Feedback FEEDBACK
Advertise with us ADVERTISE
Subscribe SUBSCRIBE
Bullet LOG OFF
                         
      The ASPToday Article
December 24, 2001
      Previous article -
December 21, 2001
   
 
   
   
   
WMI and Mobile Devices in .NET   Eric Rockenbach  
by Eric Rockenbach
 
CATEGORIES:  .NET Framework, Other Technologies  
ARTICLE TYPE: Overview Reader Comments
   
    ABSTRACT  
 

Windows Management Instrumentation (WMI) is a Microsoft technology that allows robust low-level communication with the Windows Operating System. Everything from the operating system installation serial number, to information on components running in COM+ is available in WMI. In this article Eric Rockenbach will demonstrate an application that will utilize WMI in .NET to Reboot, Shutdown, and view the CPU Usage of network computers. The Mobile Internet Toolkit will be used to bring this functionality to an array of mobile devices with a "write one application to support many" approach to mobile development.

   
                   
    Article Discussion   Rate this article   Related Links   Index Entries  
   
 
    ARTICLE

Windows Management Instrumentation (WMI) is a Microsoft technology that allows robust low-level communication with the Windows operating system. WMI has been in existence for a few years and was traditionally designed for C++ and VB developers. Everything from the operating system installation serial number, to information on components running in COM+ is available in WMI. This example will demonstrate the ability to utilize WMI in .NET to reboot, shutdown, and view the CPU usage of network computers. The Mobile Internet Toolkit will be used to bring this functionality to an array of mobile devices with a "write one application to support many" approach to mobile development.

Introduction

WMI communicates with a Windows operating system's installed devices, running processes, Win 32 Processor, COM+ components, and Win 32 system properties. WMI is queried through Windows Management Instrumentation Query Language (WQL), which is a subset of the American National Standard Query Language (ANSI SQL). Those who have written SQL database queries will be familiar with the constructors used in WQL, such as FROM , WHERE , AND , OR , and *. .NET supports full WMI implementation through the System.Management assembly. The sample application allows users to monitor and control network computers through a mobile device using WMI and Windows role-based impersonation security. .NET's Mobile Internet Toolkit (a.k.a. Mobile Web SDK) allows developers to write web pages that support HTML based Pocket PCs and WAP enabled phones. Those familiar with ASP.NET can begin authoring their own mobile web pages within minutes using Visual Studio's "Mobile Web Application" project. Web pages authored by the "Mobile Web Application" project dynamically assess the type of client viewing the page and send appropriately labeled mark-up such as WML for WAP enabled devices or HTML for regular HTML devices.

System Requirements

This article requires the sample application's server to have the .NET framework installed in conjunction with Windows NT, 2000, or XP. Machines that are requested through the sample application by the user must be connected to the network and have either Windows NT, 2000, or XP installed, but do not need to be .NET framework enabled. The mobile application is viewable though HTML browsers, HTML enabled Pocket PCs, and WAP enabled phones. It is assumed that the reader has knowledge of the .NET framework and the C# programming language.

Communicating With a Computer's Operating System

Clear communication with the computer's operating system is very important to system developers. Everything developed has a direct consequence on the operating system's behavior. Without fully considering load factors and other processes running on the computer your application may be the one that drives the server over its limit and overloads the machine. Imagine creating a setup application that requires a specific amount of system memory for proper installation, without viewing the system resources your installation may fail midway, leaving the user on the phone to their help desk as he/she attempts to walk through debugging methods. This is a good way to guarantee that your product is returned immediately! Users are typically not computer hardware or software experts and will not tolerate difficult software, especially software that does not install without hassle. When managing a network one may also want event-based alert messages for those occasions when the CPU usage spikes too high for a considerable amount of time. This is all obtainable through WMI.

WMI's popularity stems from the fact that many OS requests can be handled without having to program at an API level. Operating systems can be queried through WQL; much like the way SQL queries a database. WQL Queries might look something like this:

SELECT * FROM Win32_Processor

Some SQL keywords that may seem tempting to use, such as NULL , TRUE , and FALSE , are invalid in WQL. An important distinction between SQL and WQL is that, unlike SQL, WQL does not allow joins. A query may not include Win32_Processor with Win32_Operating System in the same SELECT statement. WMI gives the ability to view operating system behavior and, most importantly, invoke methods such as rebooting or shutting down a computer. .NET provides easy access to the WMI classes through the use of the System.Management assembly. WMI requires the installation of Windows 95, 98, ME, NT, 2000, XP, or .NET Server. .NET Server is a new platform Microsoft will release in 2002. When programming WMI for different OS flavors, keep in mind their differences and make sure to double-check the method or property called to see if it is applicable to the particular OS.

In the creation of an application that will allow users to shutdown and reboot machines and view CPU usage, we use quite an array of different classes from the System.Management namespace. The ManagementPath provides path connection information to the ManagementScope class. The ManagementScope class in turn is passed to the ManagementObject class that invokes methods against a requested machine.

The following WMI classes are used in the sample application. The ManagementObjectSearcher class allows us to perform WQL queries. WQL queries are used in the sample application to query the CPU to get its utilization. Take the time to get familiar with their properties and methods.

The ManagementPath class contains the server path properties:

Properties Used:

The ManagementObject class invokes methods against the specified server's operating system

Properties Used:

Methods Used:

The ManagementObjectSearcher c lass supports the WQL query, which retrieves information from the operating system in a collection

Properties Used:

Methods Used:

Extending applications to Mobile Devices

You've heard the marketing hype on how mobile devices will be the preferred way to access the web by year 2004, but the fact of the matter is that there are many different types of mobile devices that require many different ways of programming. Imagine creating three different applications depending on what type of device the client is using, an everyday reality for some developers. Microsoft is bridging the gap with the Mobile Internet Toolkit, which features "build it once" mobile web pages. This is still not a true one-size fits all solution, there are still issues with building application that reside on the Mobile Device's Operating System.

Microsoft's Mobile Internet Toolkit features the ability to write one web page that supports multiple mobile devices.

Image 1

Integrated development from Visual Studio's "Mobile Web Application" project makes it easy to build web pages that work for HTML-based Pocket PCs and WAP-enabled cell phone s. Simply drag and drop controls through the form designer or write pages in HTML view with help from Intellisense. These are ASP.NET pages marked with the .aspx file extension, but the mobile pages inherit from the System.Web.UI.MobileControls.MobilePage class. Using the namespace System.Web.UI.MobileControls , mobile web pages support controls that are similar to, if not the same as, regular ASP.NET controls. The difference between the mobile controls and ASP.NET web controls is mobile controls have the ability to determine the client such as a cell-phone and send WML mark-up or send HTML to a Pocket PC. Unlike ASP.NET pages the mobile pages support multiple forms because of their small size, projects would otherwise contain numerous forms for trivial tasks. Cookies may be used with mobile web pages, but not all client devices can accept them, it's best to use a query string if you are attempting to support the vast majority of devices on the market. When building a mobile web site, download emulators to view content, as it would display on the user's mobile device. OpenWave.com contains a WAP-enabled emulator for a cellular phone, you may use an HTML browser to view the pages intended for HTML-based Pocket PCs. Microsoft also has a mobile phone emulator that can be found at http://www.microsoft.com/mobile/phones/mme/?WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n

The following mobile controls are used in the sample application. Take the time to get familiar with them.

Building a Mobile Web Application that utilizes WMI to Reboot, Shutdown and View CPU Usage of Servers in an Enterprise Network.

Building a solution to reboot, shutdown and view CPU usage of any server in the network will require the user's credentials and a control panel for server administration after they have logged in. Start by opening Visual Studio and creating a Mobile Web Application project named MobileCPUControl. Add two mobile web forms entitled sysLogIn and sysControl. Right-click References in Project Explorer, add System.Management assembly, and reference the namespace with a using clause in the code behind files of each page. The example is created using a query string to keep state from page to page. For security reasons it is a good idea to encrypt the query string using cryptography if this application is to run on an enterprise network.

sysLogIn.aspx

This display page contains the mobile ASP.NET controls that allow users to enter their details and the desirable connection machine type. The TextBox control is used for txtMachine , txtPassword , and txtDomain to accept text from the user. The Command button named Command1 at the bottom of the page submits the form back to the server for processing. Visual Studio places all code, with the exception of the code embedded in the <mobile:form> tags, dynamically upon creation of the page. Notice at the top of the page, the register tag is used to register an assembly for use in your current page. All the server-side controls used in this application are prefixed with mobile , that is because it is declared at the top of the page in the tag prefix property of the register tag.

<%@ Register TagPrefix="mobile" Namespace="System.Web.UI.MobileControls" 
     Assembly="System.Web.Mobile" %>
<%@ Page language="c#" Codebehind="sysLogIn.aspx.cs" 
     Inherits="MobileCPUControl.sysLogIn" AutoEventWireup="false" %>

<meta content="Microsoft Visual Studio 7.0" name=GENERATOR>
<meta content=C# name=CODE_LANGUAGE>
<meta content="Mobile Web Page" name=vs_targetSchema>
<body xmlns:mobile="Mobile Web Form Controls">
  <mobile:form id=Form1 runat="server">

    <B>Machine:</B><BR>
    <mobile:TextBox id=txtMachine runat="server"></mobile:TextBox><BR>
    <B>UserName:</B><BR>
    <mobile:TextBox id=txtUser Runat="Server"></mobile:TextBox><BR>
    <B>Password:</B><BR>
    <mobile:TextBox id=txtPassword runat="server"></mobile:TextBox><BR>
    <B>Domain:</B><BR>
    <mobile:TextBox id=txtDomain runat="server"></mobile:TextBox><BR>
    <mobile:Command id=Command1 runat="server">Connect</mobile:Command>

  </mobile:form>
</body>

sysLogIn.aspx.cs

The code behind class file for sysLogIn.aspx contains the server logic for the login mobile page. Notice how this class inherits from the System.Web.UI.MobileControls.MobilePage. The controls from the .aspx page are referenced here as class-level protected objects. When the Command1 button is clicked, the Command1_Click method is called, which contains a redirect to the sysControl.aspx page with a query string that includes the user's credentials and machine to connect to, requested from the txtMachine , txtPassword , txtDomain , and txtMachine controls. Notice on page load, if there is a query string sent to the page containing either Mch , Dom , Usr , or Pwd , the TextBox controls for the appropriate object are set to the query string's value. This would be used when sysControl.aspx links back to this page in order for the users to change the connecting machine and their details.

private void InitializeComponent()
{
  this.Command1.Click += new System.EventHandler(this.Command1_Click);
  this.Load += new System.EventHandler(this.Page_Load);
}

private void Command1_Click(object sender, System.EventArgs e)
{
  Response.Redirect(@"http://localhost/MobileCPUControl/sysControl.aspx?"+
                    "Mch="+txtMachine.Text+"&Usr="+txtUser.Text+"
                    &Pwd="+txtPassword.Text+"&Dom="+txtDomain.Text);
}

Image 2

sysControl.aspx

This display page contains mobile ASP.NET controls within the form that allow a user to click on a link to reboot or shutdown the machine and also display the CPU usage for the requested machine. Notice the Link Control is used on txtShutdown , txtReboot , and txtMachine to perform different operations on user selection. The TextView control ActionMess displays the success or failure message back to the user. All code, except that embedded in the mobile form, is dynamically placed by Visual Studio upon creation of the page.

<%@ Register TagPrefix="mobile" Namespace="System.Web.UI.MobileControls" 
      Assembly="System.Web.Mobile" %>
<%@ Page language="c#" Codebehind="sysControl.aspx.cs" 
      Inherits="MobileCPUControl.sysControl" AutoEventWireup="false" %>

<meta content="Microsoft Visual Studio 7.0" name=GENERATOR>
<meta content=C# name=CODE_LANGUAGE>
<meta content="Mobile Web Page" name=vs_targetSchema>
<body xmlns:mobile="Mobile Web Form Controls">
  <mobile:form id=Form1 runat="server  ">

  <mobile:TextView id=ActionMess Runat="Server"></mobile:TextView>
  <mobile:Link id=txtMachine Runat="Server" Font-Bold="True"></mobile:Link>
  <mobile:Label id=txtCPU Runat="Server"></mobile:Label>
  <mobile:Link id=txtReboot Runat="Server" Text="Reboot"></mobile:Link>
  <mobile:Link id=txtShutdown Runat="Server" Text="Shutdown"></mobile:Link>

  </mobile:form>
</body>

sysControl.aspx class='codeintext'> 'codeintext'> displays a confirmation after the user has rebooted the server.

Image 3

sysControl.aspx.cs

The code behind class file for sysControl.aspx contains the server logic for the control panel mobile page. Notice how this class inherits from the System.Web.UI.MobileControls.MobilePage. Controls from the .aspx page are referenced here as class level protected objects. On page load, a new ConnectionOptions object is instantiated and passed in a username and password. Using the SetLinks method, links are set with the query strings that keep the state. The GetCPUUsage method is called with the ConnectionOptions object to retrieve the CPU usage from the requested machine and display it on the mobile page. If the user has requested to reboot or shutdown the system the query string will contain an action parameter. Check the action parameter to see that it is not null, then call the method GetAction and pass in ConnectionOptions object.

private void Page_Load(object sender, System.EventArgs e)
{
  ConnectionOptions oOpt = new ConnectionOptions();

  oOpt.Username = Request.QueryString["Dom"].ToString()+
                  "\\"+Request.QueryString["Usr"].ToString();
  oOpt.Password = Request.QueryString["Pwd"].ToString();
  oOpt.EnablePrivileges = true;
  oOpt.Impersonation = ImpersonationLevel.Impersonate;

  // Get CPU Usage and set it on page, pass is ConnectionOptions
  GetCPUUsage(oOpt);

  if(Request.QueryString["Action"] != null)
  {
    // Call Shutdown or reboot if an action is not null, pass in 
    // ConnectionOptions
    GetAction(oOpt);
  }
}

The SetLinks method sets the NavigateURL property on the mobile controls txtReboot , txtShutdown , and txtMachine to keep the query string Dom , Usr , Pwd , Mch and Action state variables when the user clicks from page to page. Mch query string variable is also set to the txtMachine.Text property to display the requested machine name back to the user.

The GetCPUUsage method establishes a path to the server requested and passes this path and the ConnectionOptions to a newly instantiated ManagementScope object. GetCPUUsage method then attempts to retrieve the LoadPercentage from the Win32_Processor using a WQL query. The ManagementObjectSearcher class executes the query by passing in the ManagementScope object and the WQL query. If the Get method of ManagementObjectSearcher succeeds the LoadPercentage is displayed on the mobile page and Dispose is called. If the Get method fails it alerts the user that it could not connect to the requested machine.

private void GetCPUUsage(ConnectionOptions oOpt)
{
    try
  {
    //Create path to view CPU Usage from
    string cPATH = @"\root\cimv2";
    string strServerPath = @"\\" + Request.QueryString["Mch"].ToString() + 
                           cPATH;

    //Instantiate a WMI class and pass in server path and ConnectionOptions
    ManagementScope objScope = new ManagementScope(strServerPath,oOpt);

    //Set a WQL query to search for usage
    SelectQuery objQuery = new SelectQuery("SELECT LoadPercentage FROM 
                                            Win32_Processor");

    //Search for CPU usage
    ManagementObjectSearcher oSearcher = 
                           new ManagementObjectSearcher(objScope, objQuery);

    foreach (ManagementObject oService in oSearcher.Get())
    {
      //Display Usage, comes back as percent
      txtCPU.Text = "CPU Usage: "+oService["LoadPercentage"].ToString()+"%";
      oService.Dispose();
    }
}
  catch(Exception e)
  {
    //If operation fails write this to the screen
    txtCPU.Text = "Could Not Connect";
  }
}

The GetAction method establishes a path to the server using the ManagementPath class and makes a call to retrieve the Boot.ini file from the requested machine. The Boot.ini file is needed to set the relative path where the Windows OS resides. After the operating system's path is retrieved from the Boot.ini it can be passed into the ManagementPath class so WMI will know what type of operating system to reboot or shutdown. It will then instantiate a new ManagementObject class, pass in the ConnectionOptions and the ManagementPath and call the InvokeMethod with the action parameter Reboot or Shutdown. It will dispose of the object after completion. If the procedure fails, the user will be alerted that it could not perform the requested action against the requested server. If the procedure succeeds, the shutdown and reboot links are hidden and the user is alerted that the requested action was successful to assure that the client will not continue attempting to shutdown or reboot the machine.

private void GetAction(ConnectionOptions oOpt)
{
  try
  {
    //Instantiate a new WMI ManagementPath class
    ManagementPath path = new ManagementPath();

    //Bind a server and boot location to path
    path.Server = Request.QueryString["Mch"].ToString().Trim();
    path.NamespacePath = @"root\CIMV2";
    path.RelativePath = GetBootini();

    //Instantiate a new WMI ManagementObject class
    ManagementObject o = new ManagementObject(path);
      
    ManagementBaseObject inParams = null;

    //Bind ConnectionOptions to the ManagementObject
    o.Scope.Options = oOpt;

    //Invoke the method that will 'shutdown' or 'reboot' machine
    ManagementBaseObject outParams = o.InvokeMethod(  Request.QueryString
["Action"].ToString(), inParams, null);

    //Dispose of the object
    o.Dispose();

    //Don't allow users to attempt to make another call right away 
    //to the OS if call suceeded
    txtReboot.Visible = false;
    txtShutdown.Visible = false;

    //Display Action(Shutdown, Reboot) is progress
    ActionMess.Text = Request.QueryString["Mch"].ToString()+" "+
          Request.QueryString["Action"].ToString()+" In Progress";

  }
  catch(Exception e)
  {
    //If operation fails let user know
  ActionMess.Text = "Could Not "+ Request.QueryString["Action"].ToString()+" 
                    "+ Request.QueryString["Mch"].ToString();
  }
}

The GetBootini method begins by impersonating the user and making a call to the System.IO.StreamReader class and passes in the location of the Boot.ini file. Reboot or shutdown the system by reading the Boot.ini file, using the ReadToEnd method. Parse the file to get the name of the OS, where the OS physically resides, total partition count, and total hard disk count of the OS. After the parameters are captured, reassemble the string that will be used to find the relative OS path. The relative path string will look something like this:

"Windows 2000 Professional|C:\\WINNT|\\Device\\Harddisk0\\Partition1"

The Boot.ini file is found on all Windows operating systems at the root of where Windows is installed. If Windows was installed on the C: drive the Boot.ini file will be located at C:\Boot.ini. If the Boot.ini file is not visible there is a possibility that it is hidden, go to Start | Settings | Control Panel | Folder Options and choose Show Hidden Files and Folders from the view tab.

The sample application currently supports one Windows operating system. If there is a need to extend it, attempt to find two Boot.ini files. If two are found try to perform the action to the Operating System with one Boot.ini file's path. If this fails, then try the other Boot.ini file's path.

The screenshot below shows an example Boot.ini file from a Windows 2000 Professional Operating System.

Image 4

Completed Mobile Web Application

The completed application can be viewed with WAP-enabled cell phones, HTML-based Pocket PCs, emulators, and a regular HTML browser by directing the address to http://localhost/MobileCPUControl/sysLogin.aspx. Replace localhost in all URLs to the name of the server hosting the application, and build the project.

You should now be able to remotely shutdown, reboot, and view the CPU usage of any Windows based server on your network, even those that are not .NET framework enabled.

Conclusion

Using WMI and WQL to create an application that truly gives power to a mobile device is fairly painless in .NET. There is no need to build three different versions for each small device platform, nor is there a need to call tricky system APIs to shutdown or reboot a computer.

When creating applications for mobile devices to give the user more power, keep in mind that just because the device is fairly powerless, such as a cell phone, the application doesn't have to be.

Related Links

Microsoft's Mobile Internet Toolkit:

http://msdn.microsoft.com/vstudio/nextgen/device/mitdefault.asp?WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n

Microsoft's Mobile Phones Emulator:

http://www.microsoft.com/mobile/phones/mme/?WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n

OpenWave's Cell Phone Emulator and SDK:

http://developer.openwave.com/support/techlib.html#gettingstarted?WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n

Microsoft's WMI page:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/r_32os4_0h7x.asp&WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n

Microsoft's WQL page:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/us_query_84oc.asp&WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n

Mobile Web Applications Quick Start Tutorial:

http://localhost/mobilequickstart/?WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n

Impersonation based Security through the LOGON32 system API:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dn_voices_askgui/html/askgui08282001.asp&WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n

 
 
   
  RATE THIS ARTICLE
  Please rate this article (1-5). Was this article...
 
 
Useful? No Yes, Very
 
Innovative? No Yes, Very
 
Informative? No Yes, Very
 
Brief Reader Comments?
Your Name:
(Optional)
 
  USEFUL LINKS
  Related Tasks:
 
 
   
 
  Related Sources
 
  • OpenWave's Cell Phone Emulator and SDK:: http://developer.openwave.com/support/techlib.html#gettingstarted
  • Impersonation based Security through the LOGON32 system API:: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dn_voices_askgui/html/askgui08282001.asp
  • Microsoft's WMI page:: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/r_32os4_0h7x.asp
  • Microsoft's WQL page:: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/us_query_84oc.asp
  • Microsoft's Mobile Internet Toolkit:: http://msdn.microsoft.com/vstudio/nextgen/device/mitdefault.asp
  • Microsoft's Mobile Phones Emulator:: http://www.microsoft.com/mobile/phones/mme/
  •  
     
           
      Search the ASPToday Living Book   ASPToday Living Book
     
      Index Full Text Advanced 
     
     
           
      Index Entries in this Article
     
  • .NET Framework
  •  
  • alert system
  •  
  • Boot.ini file
  •  
  • building applications that reside on OS
  •  
  • Command control
  •  
  • communicating with OS
  •  
  • ConnectionOptions object
  •  
  • description
  •  
  • display page
  •  
  • displaying CPU usage
  •  
  • Dispose method
  •  
  • extending applications to Mobile Devices
  •  
  • Form control
  •  
  • GET method
  •  
  • implementing
  •  
  • InvokeMethod method
  •  
  • Label control
  •  
  • limitations
  •  
  • Link control
  •  
  • login mobile page
  •  
  • ManagementObject class
  •  
  • ManagementObjectSearcher class
  •  
  • ManagementPath class
  •  
  • ManagementScope class
  •  
  • mobile controls
  •  
  • Mobile Devices
  •  
  • Mobile Internet Toolkit
  •  
  • Mobile Web Application
  •  
  • Mobile Web Application project
  •  
  • MobilePage class
  •  
  • MS Mobile Phones Emulator
  •  
  • NamespacePath property
  •  
  • NavigateUrl property
  •  
  • OpenWave Cell Phone Emulator
  •  
  • Options property
  •  
  • Path property
  •  
  • Query property
  •  
  • querying WMI through WQL
  •  
  • RelativePath property
  •  
  • Scope property
  •  
  • Server property
  •  
  • SQL
  •  
  • StreamReader object
  •  
  • System.IO namespace
  •  
  • System.Management namespace
  •  
  • System.Web.UI.MobileControls namespace
  •  
  • TextView control
  •  
  • using in .NET
  •  
  • Visual Studio .NET
  •  
  • WAP emulators
  •  
  • web applications
  •  
  • WMI
  •  
  • WMI classes
  •  
  • WMI implementation
  •  
  • WQL
  •  
  • write one application to support many
  •  
     
     
    HOME | SITE MAP | INDEX | SEARCH | REFERENCE | FEEDBACK | ADVERTISE | SUBSCRIBE
    .NET Framework Components Data Access DNA 2000 E-commerce Performance
    Security Admin Site Design Scripting XML/Data Transfer Other Technologies

     
    ASPToday is brought to you by Wrox Press (http://www.asptoday.com/OffSiteRedirect.asp?Advertiser=www.wrox.com/&WROXEMPTOKEN=687483ZQWSeFRKdKFxKVj6UC2n). Please see our terms and conditions and privacy policy.
    ASPToday is optimised for Microsoft Internet Explorer 5 browsers.
    Please report any website problems to webmaster@asptoday.com. Copyright © 2001 Wrox Press. All Rights Reserved.