Building a multitier application can be a daunting task. Implementing the application logic is just one part of the development process. Multitier applications require a sophisticated infrastructure. This infrastructure often includes code to synchronize access to server resources, manage server connections and thread pools, enforce security, and so on.
Without using Microsoft Transaction Server (MTS), application developers must build the infrastructure for multitier applications themselves. However, by using MTS, developers can take advantage of the infrastructure that MTS provides and are free to concentrate on implementing the application instead of its infrastructure.
Today you will learn
NOTE |
Today's development work utilizes Microsoft Transaction Server (MTS), Microsoft Internet Information Sever (IIS), and Internet Explorer version 4.0 (IE4). If you have access to MTS, IE4, and IIS (or Microsoft Personal Web Server, which is compatible with IIS), you will be able to perform all the programming tasks described today. If not, you will have to learn some of these techniques without being able to try them yourself. |
The move to multitier applications is being driven by the need for business applications that
Building applications with these characteristics requires lots of code. The infrastructure, or plumbing (which includes the code for communications, resource allocation, security, and so on), has to be in place before the developer can build the application.
Developers who build multitier applications from scratch have found that building the infrastructure can be the most challenging and time-consuming portion of the project. In other words, building the plumbing can be a significant part of the development effort.
The plumbing for a multitier application typically must include code that
This plumbing has to exist for multitier applications to work. That means that someone has to write it.
Developers who build their own plumbing for multitier applications typically must rebuild the plumbing (or parts of it) each time they build a new application. For each application, they must spend a significant portion of their time writing code that is not really part of the application.
Developers who build their own plumbing also have limited opportunities to use third-party components in their applications. For the use of third-party components in multitier applications, a common component framework must be in place, and the components and the plumbing must be compatible.
The Component Object Model (COM) provides a component framework. However, COM by itself does not provide sufficient plumbing for building multitier applications. In practice, COM by itself provides the framework for only a single tier of an application.
NOTE |
DCOM does provide the means to instantiate COM objects on different machines across a LAN. However, this capability alone does not provide a complete foundation for building multitier applications. Instantiating objects on a server is only part of the plumbing that a multitier application needs. |
COM provides a component framework for a single tier in an application. To use COM components in a multitier application, the application must have plumbing that is compatible with COM.
MTS provides plumbing for multitier applications that is compatible with COM. COM provides the framework for using components in a single tier, and MTS provides the plumbing for these components to be used across tiers in a multitier application.
Microsoft Transaction Server extends COM to provide a multitier application framework that can shelter you from the complexities of multitier development. MTS handles object instantiation at the server, process and thread management, synchronization of shared resources, and security. MTS also supports transactions to ensure atomic updates and consistency.
MTS is included in the Windows NT 4.0 Option Pack. MTS requires
You can install MTS on your computer by using the Windows NT 4.0 Option Pack Setup program. You can install MTS with or without IIS and the other Option Pack components.
To install MTS
The Windows NT 4.0 Option Pack and MTS also are available as part of the Visual Studio Enterprise Edition Server Setup options. Select Server Setup; then select BackOffice Setup. Then you can select NT Option Pack as an installation option (along with SQL server, and so on.)
To use MTS in a multitier application, you build the business logic and functionality of your application into a set of COM in-process servers. (An in-process COM server is a COM server that resides in a DLL.) Each of these COM servers performs some function or well-defined set of functions in your application.
The client programs in your application call these COM servers. The client programs do not perform any communications with a database server directly. The COM servers constitute a middle tier that the client programs call. This multitier architecture is illustrated in Figure 12.1.
Figure 12.1 : Multitier architecture.
In-process COM servers by themselves can't provide an adequate middle tier. MTS extends COM by providing a wrapper for COM servers. You install these in-process COM servers in MTS, and MTS wraps them and gives them additional functionality. This additional functionality makes it possible for these COM servers to be used effectively as a middle tier in a multitier application. Figure 12.2 illustrates the role of MTS in multitier applications.
Figure 12.2 : MTS in multitier applications.
MTS wraps and extends the COM servers by intercepting the calls to them from client applications. MTS does this by causing the Registry entries for COM servers to point to the MTS runtime environment, instead of to the COM server's DLL. You will recall from Day 9, "Understanding COM," that the location of a COM server's binary (EXE or DLL) is stored in the Registry. Figure 12.3 shows a sample Registry entry for an in-process COM.
Figure 12.3 : The Registry entry for an in-process COM server.
You can see in Figure 12.3 that the InprocServer32 key for this particular CLSID points to RDSATLMTS1.DLL (the filename derives from the fact that this example experiments with RDS, ATL, and MTS) in the WorkInProgress\RDSATLMTS1\Debug directory on the C drive.
As you will recall from Day 9, when a client application requests a pointer to an interface to a COM server (using the CLSID), the system locates the binary and loads it if necessary. The system then calls the class factory in the binary to create an instance of the COM server (if an instance needs to be created) and returns to the client a pointer to an interface to the COM server. (Refer to Figure 9.5 for an illustration of this process.)
When you install an in-process COM server in MTS, the COM server becomes an MTS component. The Registry entry for that COM server is changed so that it no longer points to the DLL in which the COM server resides. Instead, the Registry points to MTX.EXE, which is the MTS application binary. This is illustrated in Figure 12.4.
Figure 12.4 : The Registry entry for an in-process COM server installed in MTS.
You can see in Figure 12.4 that a LocalServer32 key that points to C:\WINNT\System32\mtx.exe has been added. Following the filename and pathname is a /p: command-line argument followed by a GUID. You can't see it in Figure 12.4, but the entry for the InprocServer32 key has been emptied so that it no longer points to the COM server's DLL.
This change in the Registry means that when a client application references this COM server's CLSID to obtain an interface pointer to the COM server, the system does not look for and load the COM server's DLL. Instead, MTX.EXE is located by the system (and loaded if necessary) and is passed the /p: command-line argument followed by the GUID shown in Figure 12.4.
Remember that an MTS component is a COM server that has been installed in MTS. The GUID passed on the command line represents the particular MTS package that contains this MTS component. An MTS package is a collection of MTS components that run in the same process.
MTS components are organized into MTS packages. Packages in MTS enable process isolation and security to be established for sets of components. Process isolation ensures that a poorly written component, which could cause errors at runtime, cannot bring down other processes running on that machine. MTS package security ensures that only authorized users can access your application.
To learn about MTS packages, and to begin today's development work, you will need to create your own package in MTS. Run the Transaction Server Explorer, which (on NT) you can run by selecting the Start, Programs, Windows NT 4.0 Option Pack, Microsoft Transaction Server, Transaction Server Explorer menu. The Transaction Server Explorer runs inside the Microsoft Management Console and is shown in Figure 12.5.
Figure 12.5 : The Transaction Server Explorer.
The tree control in the left pane enables you to navigate among the various MTS objects. You can double-click each element (or click the plus sign beside each element) in the tree control to expand it to view its children. The Transaction Server Explorer in Figure 12.5 has been expanded to show installed MTS packages.
Create your own package in MTS by right-clicking the Packages Installed folder and selecting the New Packages menu. You are presented with the first window of a wizard that will help you create your package. This window is shown in Figure 12.6.
Figure 12.6 : Using Package Wizard to create an empty MTS package.
When you install a COM server in MTS, you specify which package it is to be installed in. You can create new packages anytime you need to in MTS.
NOTE |
MTS security is not as fully implemented under Windows 95 and Windows 98 as it is under Windows NT. The following explanation of how to create your own MTS package under NT might contain steps for setting up security that do not apply under Windows 95 and Windows 98. |
Click the Create an Empty Package button. In the next window, you are prompted for the name of the package, which you can call MyFirst or some other appropriate name. Press the Next button. You will then be presented with a window to specify the identity under which this package will run. This window is shown in Figure 12.7.
Figure 12.7 : Using Package Wizard to set the package identity.
The default is to run the package under the currently logged-on user's identity. The alternative is to hard-code the package to always run under a user identity that you specify. For your work today, use the default setting of the current user's identity for your package. Click the Finish button to create the package.
Your package should appear in the tree control in the Transaction Server Explorer. Select it by clicking it, and you should see that the package contains two folders, one for components and one for roles. The Components folder will contain the in-process COM server that you will create and install later today. The Roles folder enables you to specify security to limit user access to the package and its components.
You can set the properties of your MTS package by right-clicking its element in the tree control and selecting the Properties menu from the context menu. This will show the Properties window for your package. The contents of the General tab are shown in Figure 12.8.
Figure 12.8 : Package properties- the General tab.
The Properties window in Figure 12.8 shows the name of the package, a description (which you can assign), and the GUID that uniquely identifies this package. This is the GUID that appears in the Registry as the /p: argument with MTX.EXE for the COM servers installed in this package.
Select the Security tab, which enables you to turn on and off security for this package. There is a check box titled Enable Authorization Checking that you should leave unchecked to leave security off for your package while you are learning MTS.
The Advanced tab enables you to specify how long the components in your package should live on the server after being used and released by client applications. The default values are fine for now.
The Identity tab is the same one you saw on the Package Wizard. Use the default setting of the current user's identity for your package.
The Activation tab enables you to specify where (in which process) the components in your package should be instantiated. The Activation tab is shown in Figure 12.9.
Figure 12.9 : Package properties- the Activation tab.
The Library Package activation type is available only for client applications that run on the computer on which the package and components are installed. In other words, the client application and the components have to be on the same machine. Selecting this option causes the components to be instantiated in the client application's address space.
The Server Package activation type causes the components to be instantiated in a server process on the MTS machine. In other words, the package runs in its own process on the MTS machine. The Server Package activation type provides process isolation. It also enables MTS to enforce security, manage resource sharing, and track the usage of components in the package. Select Server Package activation for your package.
Now it is time to create your first MTS component. Run Visual Studio and select the File, New menu. On the Project tab, specify an ATL COM AppWizard project. The New Project window is shown in Figure 12.10.
Figure 12.10: The new ATL COM AppWizard project window.
Call the project MTSComp1, as shown in Figure 12.10, or use whatever name you think is appropriate. Figure 12.10 shows the project being placed in the c:\tysdbvc\mtscomp1 directory. If that directory already exists on your machine, you will need to use a different directory. Click the OK button.
The next window is the ATL COM AppWizard-Step 1 of 1 window. It is shown in Figure 12.11.
Figure 12.11: The ATL COM AppWizard-Step 1 of 1 window.
Specify merging of proxy/stub code and support for MTS, as shown in Figure 12.11. Click the Finish button and then the OK button for the wizard to create the project.
Select the Insert menu and the New ATL Object menu choice to specify an MS Transaction Server object as shown in Figure 12.12.
Figure 12.12: Using the ATL Object Wizard.
Click the Next button. The ATL Object Wizard Properties window will appear. Under the Names tab, specify a short name of Component1.
Under the MTS tab, specify a dual interface. Also, enable the check boxes for Support IObjectControl and Can Be Pooled, as shown in Figure 12.13.
Figure 12.13: The ATL Object Wizard Properties window and the MTS tab.
The IObjectControl and Can Be Pooled options tell MTS that your component should be returned to an instance pool after deactivation, rather than be destroyed. This enables MTS to provide access to your component in a very efficient manner as multiple client applications create and free your object. MTS provides efficient access to your component by using thread pooling and database connection pooling.
Click the OK button to generate the object. The CComponent1 class will appear in the tree view in the ClassView tab of the project workspace in Visual Studio. The IComponent1 interface will also appear there.
To add a function to your component, right-click the IComponent1 interface and choose Add Method from the context menu. The Add Method to Interface dialog will be displayed, as shown in Figure 12.14.
Figure 12.14: Adding the ReturnHello method to an interface.
As Figure 12.14 shows, you should specify a method name of ReturnHello and the following parameters:
[out, retval] BSTR* bstrHello
Using [out, retval] tells the system that the bstrHello parameter needs to be marshaled from the server to the client and that this parameter is the return value of the function.
Click the OK button to add the method. Click the plus sign next to the IComponent1 interface, and you should see the ReturnHello method listed. Double-click the ReturnHello method to view the MTSComp1.IDL file that the ATL Wizard created for you. This IDL code declares the interface to IComponent1.
Click the plus sign next to the CComponent1 class. The IComponent1 interface will be listed there, as well as four other functions and a data member for the CComponent1 class. Click the plus sign next to the IComponent1 interface under the CComponent1 class, and you will see the ReturnHello function listed there, as shown in Figure 12.15.
Figure 12.15: The ReturnHello function in Component1.CPP.
Double-click the ReturnHello function under the IComponent1
interface under the CComponent1 class to open the Component1.CPP,
as shown in Figure 12.15. This is where you implement the code
for IComponent1. Change the code in the ReturnHello
function so that it matches the code shown in Listing 12.1.
Listing 12.1 The RETURNHELLO Function in Component1.CPP
1: STDMETHODIMP CComponent1::ReturnHello(BSTR *bstrHello) 2: { 3: *bstrHello = _bstr_t("Hello"); 4: return S_OK; 5: }
The ATL Object Wizard generated lines 1, 2, 4, and 5. You add the code in line 3 that assigns a temporary _bstr_t instance to the pMyBSTR argument. You will also need to add
#include <comdef.h>
to Component1.CPP so that you can use the _bstr_t class. Build the project, which should compile and link with no errors or warnings. When you build the project, Visual Studio will register the COM server for you.
NOTE |
To build any of the release versions of this project, you might need to remove the _ATL_MIN_CRT preprocessor definition from the compiler settings for those configurations. Search your Visual C++ documentation on _ATL_MIN_CRT, for details. |
Before installing your component in MTS, you would be wise to test it. There are several ways for you to verify that your COM component works.
First, you will want to ensure that the system can instantiate your component. The easiest way to do this is to use the OLE-COM Object Viewer. You will recall from Day 10, "Database Client Technologies and the Secrets of ADO," that you can find the OLE-COM Object Viewer under the Microsoft Visual Studio 6.0 Tools menu. The menu OLE-COM Viewer is called OLE View. Run the OLE-COM Object viewer and expand the Object Classes, Grouped by Component Category elements of the tree control in the left pane. Expand the Automation Objects element. Under the Automation Objects in the left pane, you should see all the Automation objects that are registered on your system.
Scroll down until you find Component1 Class. If you don't see Component1 there, it probably is not registered. Visual Studio registers it for you after every successful build, so it should be there if you are doing this on your build machine. If you ever need to register the component manually (perhaps on another machine), you can run regsvr32 mtscomp1.DLL from the DOS prompt to register it.
Click Component1 Class, and the right pane of the OLE-COM Viewer will display the Registry entry for your Component1. Right-click Component1 in the left pane and select Create Instance from the context menu. The Component1 Class element in the tree control will appear in bold text, and the IComponent1, IDispatch, and IUnkown interfaces will appear under it, as shown in Figure 12.16.
Figure 12.16: Using the OLE-COM viewer to instantiate a COM object.
If this works, you know that your Component1 is registered properly and that the system can instantiate it. Release the instance of the Component1 you just created, by right-clicking Component1 Class and selecting Release Instance from the context menu.
One of the easiest ways to exercise a function in your component is to write a simple script that calls it.
Until recently, the only scripting language that Windows supported was the MS-DOS command language. Compared to the scripting languages available on other platforms, the MS-DOS command language has very limited features and capabilities.
Microsoft has greatly expanded the scripting language support on the Win32 platform by introducing the ActiveX scripting architecture. Microsoft provides three hosts for running scripts in the ActiveX scripting architecture:
The Windows-based host is called WSCRIPT.EXE and enables you to run scripts directly from the Windows desktop by double-clicking a script file. The command prompt-based host is CSCRIPT.EXE and enables you to run scripts from the MS-DOS prompt by simply entering the name of the script file.
WSH is included in the NT 4.0 Option Pack, Windows 98, Windows NT Workstation version 5.0, and Windows NT Server version 5.0.
Microsoft provides Visual Basic Script (VBScript) and Java Script scripting engines for WSH. Support for other script languages, such as Perl, TCL, REXX, and Python, is (or will be) provided for WSH by third-party companies.
To write a script that exercises your component, create a text
file and save it as Comptest.VBS. The VBS extension is
important because the extension tells WSH which scripting language
to use. (VBS stands for VBScript.) Enter the code shown in Listing
12.2 in Comptest.VBS.
Listing 12.2 The VBScript File for Exercising COMPONENT1
1: dim comp1 2: set comp1 = WScript.CreateObject("MTSComp1.Component1.1") 3: myStr = comp1.ReturnHello 4: WScript.Echo "Result of ReturnHello: " & myStr
Line 1 in Listing 12.2 defines a variable called comp1 that can hold a COM object. Line 2 calls the WScript.CreateObject function to instantiate your component. Line 3 declares a variable called myStr and stores the results of a call to your component's ReturnHello function. Line 4 displays the resulting string in a message box.
You can execute your Comptest.VBS script by double-clicking the file in Windows Explorer or by entering cscript Comptest.VBS at the DOS prompt in the directory that contains the file. It should run with no errors and display Result of ReturnHello: Hello in a message box. Testing your component with a script is one way for you to know that your component is registered properly and that the code for your component works.
You can also call your component from within an Active Server Page. However, you must make some additional entries in the Registry for IE4 to allow your component to run in its process.
IE4 has its own security to help ensure that the ActiveX (COM)
components it runs are not malicious. A component must be registered
as safe for scripting and safe for initialization, or IE4 will
not run it. Therefore, you need to register Component1
as being safe for scripting and initialization. You do this by
creating a text file that has a .REG extension, such as marksafe.REG,
and that contains the code shown in Listing 12.3.
Listing 12.3 A REG File to Mark Your Component Safe
for Scripting and Initialization
1: REGEDIT4 2: [HKEY_CLASSES_ROOT\CLSID\<GUID>\Implemented Categories\ {7DD95801-9882-11CF-9FA9-00AA006C42C4}] 3: [HKEY_CLASSES_ROOT\CLSID\<GUID>\Implemented Categories\ {7DD95802-9882-11CF-9FA9-00AA006C42C4}]
Replace the text that says <GUID> in lines 2 and 3 of Listing 12.3 with the CLSID for your Component1. You can obtain the CLSID for your Component1 from several places.
One easy way to obtain the CLSID for your Component1 is to use the OLE-COM Viewer. Right-click Component1 Class and select Copy CLSID to Clipboard from the context menu. (After doing this you can exit OLE-COM Viewer.)
You can then paste the CLSID into the REG file. To add these entries to the Registry database on your machine, simply double-click the REG file in Windows Explorer.
In Visual Studio, create a new Active Server Page (the file does
not need to be added to a project). Enter the code shown in Listing
12.4 and save the file as ClientTierForMTS.ASP.
Listing 12.4 The ClientTierForMTS.ASP to Run COMPONENT1
from an Active Server Page
1: <HTML> 2: <HEAD> 3: <TITLE>Client Tier</TITLE> 4: </HEAD> 5: <CENTER> 6: <H1>Browser Client for MTS Component</H1> 7: 8: <!-- RDS.DataSpace --><OBJECT ID="ADS1" WIDTH=1 HEIGHT=1 9: CLASSID="CLSID:BD96C556-65A3-11D0-983A-00C04FC29E36"> 10: </OBJECT> 11: 12: <BR> 13: <BR> 14: <INPUT TYPE=BUTTON NAME="ReturnHello" VALUE="Return Hello"> 15: <INPUT NAME=ReturnedHello SIZE=10> 16: </CENTER> 17: 18: <SCRIPT LANGUAGE= "VBScript"> 19: 20: SUB ReturnHello_OnClick 21: 22: Dim objMyCustomBusinessObject 23: Set objMyCustomBusinessObject = ADS1.CreateObject("MTSComp1.Component1.1", "") 24: 25: ReturnedHello.Value = objMyCustomBusinessObject.ReturnHello 26: 27: Set objMyCustomBusinessObject = Nothing 28: 29: END SUB 30: 31: </SCRIPT> 32: </BODY> 33: </HTML>
Lines 1- 7 and 32-33 are simple HTML. Lines 11-17 contain normal HTML code that creates a ReturnHello button and a text box.
Lines 8-10 create an instance of the Remote Data Services (RDS) DataSpace object. The database space object is used to create client-side proxies of COM components that exist on a server or middle-tier machine. RDS supports four protocols over which it can instantiate and call COM objects: HTTP, HTTPS, DCOM, and in-process.
Lines 18 through 31 contain VBScript code that uses the DataSpace object to instantiate your Component1, call its ReturnHello method, and display the results in the text box.
Line 23 calls the DataSpace CreateObject function to instantiate Component1. As you can see, the second argument passed to the CreateObject function is "". This tells the CreateObject function that the COM component is installed on this machine and to run it as an in-process COM server. You can specify a machine name in the form, "machine name" to have the CreateObject function instantiate the COM component over DCOM. You can specify "HTTPS://webservername" or "HTTP://webservername" to have the CreateObject function instantiate the COM component over an HTTP connection to a Web server.
Before you can have the DataSpace CreateObject function instantiate your component over HTTP or DCOM, you must add the Prog ID of your component (MTSComp1.Component1.1) as a key under the following Registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\ADCLaunch
To run the ASP shown in Listing 12.4, place the ASP file in a directory that is accessible to IIS (IIS will need to be running on the machine). Run IE4 and enter the HTTP address of that directory, followed by the ASP filename. The page should load with no errors. When you click the ReturnHello button, Component1 will be called, and Hello will be displayed in the text box.
Note that you have not yet installed your component in MTS. The ability to instantiate and call your COM component over DCOM and HTTP from a browser is provided by RDS. When you install your component in MTS, it will not change the ASP code in Listing 12.4. Rather, MTS will simply make your Component1 more robust and secure when it executes on the middle-tier machine.
Now you need to install your COM server in MTS. Click the plus sign next to the MyFirst package in Transaction Server Explorer; then click the Components folder under the MyFirst package in the tree control. Right-click the Components folder and select the New Component menu. This will run the Component Wizard, shown in Figure 12.17.
Figure 12.17: The MTS Component Wizard.
As you can see in Figure 12.17, there is a button to install new components and a button to import components that are already registered. The button to import components that are already registered will open a window that displays all the COM servers registered on your computer. The button to install new components opens the window shown in Figure 12.18. Click the button to install new components.
Figure 12.18: The Install Components window in the MTS Component Wizard.
Click the Add Files button and navigate to the directory that contains the MTSComp1.DLL file for the COM server that you just built and select the DLL. The Install Components window should show that it found Component1, which is the COM server in your DLL. Click the Finish button to install Component1 in MTS.
You should now be able to see MTSComp1.Component1 in the MyFirst package. The installed component looks like a bowling ball with an X on it, as shown in Figure 12.19.
Figure 12.19: The MTSComp1.Compone nt1 in the Transaction Server Explorer.
Using the View menu, you can see various kinds of information about the components. One interesting view is the Status view. Select the Status view, and you will be able to see information on whether MTSComp1.Component1 has been activated, how many objects of it are instantiated, and how many are handling calls from client applications. These values should all be blank now, but when a client application calls your MTSComp1.Component1, you will be able to see that the object is instantiated by using this view.
You can use MTS components in Active Server Pages (ASP). However, there are two things you need to do to your Component1 MTS component to enable it to be used from an ASP.
First, just to ensure that the MTS security doesn't get in your way during development, you need to make sure it is not enabled for your Component1. You do this by right-clicking the bowling ball for Component1 and selecting Properties from its context menu. Under the Security tab, uncheck the Enable Authorization Checking check box, as shown in Figure 12.20, and press the OK button.
Figure 12.20: Disabling security authorization checking for Component1.
Secondly, you need to mark the component safe for scripting and initialization again. When you installed Component1 in MTS, the Registry entry was modified, and the entries that mark Component1 as safe for scripting and initialization were removed. You can put the safe-for-scripting and initialization entries back in the Registry for your component by simply double-clicking the marksafe.REG file in Windows Explorer. After you mark Component1 safe again, you might want to refresh the MyFirst package in Transaction Server Explorer, just to make sure that MTS is feeling good about things with your component.
It is important to refresh the MTS settings for components each time you recompile your project or make changes to its Registry settings. Refreshing component settings prevents your component Registry settings from being rewritten.
To refresh your component settings, in the left pane of the Transaction Server Explorer, select My Computer. On the Action menu, click Refresh All Components. This updates MTS with any changes to the System Registry, component CLSIDs, or interface identifiers (IIDs). You can also refresh components by selecting the computer in the left pane of the Explorer and clicking the Refresh button on the MTS toolbar.
You can refresh individual packages in MTS by right-clicking the Components folder, under the package in the left pane of the Transaction Server Explorer, and selecting Refresh from the context menu.
When you build your MTSComp1 project, it will run mtxrereg.EXE as a postbuild step. Mtxrereg.EXE is a command-line version of the Refresh Components menu that you can access by right-clicking a package. If mtxrereg.EXE runs properly, you will not have to use Transaction Server Explorer to refresh your component after each build.
After making all these changes, your component will be ready to run under MTS and will be accessible to Web browsers through RDS.
Run the ASP shown in Listing 12.4 again, by clicking the Refresh button on your browser. If you have already quit IE4, run it and enter the HTTP address of that directory, followed by the ASP filename. The page should load with no errors. When you click the ReturnHello button, Component1 will be called, and Hello will be displayed in the text box.
You will also be able to look at the Status view for the components of your package in Transaction Server Explorer. The Objects, Activated, and In Call columns for MTSComp1.Component1 should go from blank to 0, indicating that the object was instantiated, activated, and called. (It ran to completion too quickly to register a 1 in any of those columns.)
Now that you know that your component can run in MTS and can be called from an ASP, it's time to make your component do something useful. In fact, your component will do more than something useful; it will do something really cool.
As you know, ADO Recordsets are built to encapsulate the results of database queries. ADO Recordsets have functions built in for navigating through records and accessing the data in fields. You can edit the data in the Recordset, and those changes will be made to data that resides in the database. (Of course, you recall that you need to make a connection to the database first by using the ADO Connection object.)
ADO Recordsets are nifty because they can be disconnected from the database and returned from functions in COM components. This is because ADO Recordsets have the capability to be marshaled across process boundaries.
This feature of ADO Recordsets enables your middle-tier COM component to create a Recordset from a database query and then send the Recordset across the wire, using COM, DCOM, or HTTP, to the IE4 Web browser. A Recordset that is sent across the wire in this manner is called a disconnected Recordset.
IE4 can use a disconnected Recordset as the data source for a grid control. The browser can accept the user's changes to the data in the grid control and then submit the Recordset back to the middle-tier component, which could submit the Recordset back to the database for the user's changes to be made to the data in the database.
Web browsers that have no direct connection to the database can retrieve and edit data from the database as if they did have a connection to it. Remember, MTS can provide security for these types of applications.
Unfortunately, building a complete application like this is beyond the scope of today's work. You will, however, in today's work write the code to send a Recordset from your middle-tier MTS component to a Web browser and bind it to a grid control.
Add another method to your Component1 interface. Name the method ReturnRs and use the following for its parameters:
[out, retval] IDispatch ** Rs
After adding the ReturnRs method, implement the code
for it as shown in Listing 12.5.
Listing 12.5 The RETURNRS method for Returning an
ADO Recordset from an MTS Component to an ASP
1: STDMETHODIMP CComponent1::ReturnRs(IDispatch **Rs) 2: { 3: _RecordsetPtr adoRs = NULL; 4: 5: try 6: { 7: adoRs.CreateInstance(__uuidof(Recordset)); 8: adoRs->PutCursorLocation(adUseClient); 9: adoRs->Open( "SELECT * FROM Customers", 10: "DSN=OrdersDb; UID=; PWD=;", 11: adOpenStatic, adLockBatchOptimistic, adCmdText ); 12: 13: *Rs = (IDispatch*)adoRs; 14: 15: adoRs->AddRef(); 16: 17: //m_spObjectContext->SetComplete(); 18: 19: return S_OK; 20: } 21: catch (_com_error e) 22: { 23: ::MessageBeep(MB_OK); 24: AtlReportError( CLSID_Component1, (LPCOLESTR)e.Description(), 25: IID_IComponent1, e.Error()); 26: } 27: 28: //m_spObjectContext->SetAbort(); 29: return E_FAIL; 30: }
Line 3 of Listing 12.5 defines an ADO Recordset smart pointer. Line 7 uses the smart pointer class's CreateInstance function to create an ADO Recordset. Line 8 tells ADO to use a client-side cursor, which is necessary if you are going to make this a disconnected Recordset. Lines 9-11 open the Recordset with a "SELECT * FROM Customers" query.
Line 13 is where the real magic happens. It casts the Recordset smart pointer as an IDispatch pointer and assigns it to the retval out parameter. When this line of code executes, COM uses the ADO Recordset's IMarshal implementation to send the Recordset object (code, data, and all) to whichever client program is calling this ReturnRs function. After line 13 executes, the Recordset no longer exists in this process space, so it is not necessary to close or release it.
Line 15 calls AddRef()so that when the smart pointer goes out of scope, it won't call Release() on the Recordset. It will cause an error if it does, because the Recordset is already gone. (It was sent to the client and released from this end in Line 13.) Line 17 could call SetComplete() if you were supporting MTS transactions in this component, which you are not. Lines 21-29 are for error handling. Line 23 just beeps in case the client application is not set up to take the output of AtlReportError().
You will need to add the #import directive for the ADO library to a source file in your project. Stdafx.h is a good file to add the #import directive to. (You will, of course, need to list the path to the MSADO15.DLL file on your machine.)
#import "C:\program files\common files\system\ado\msado15.dll" \ no_namespace \ rename( "EOF", "adoEOF" )
You will receive a C4530 warning unless you enable exception handling for this project. In the C++ Language Category of the C/C++ tab in the Project Settings dialog box, select the Enable Exception Handling option or simply use the /GX compiler switch.
After making these additions, build your MTSComp1 project. It should build with no errors or warnings. If mtxrereg.EXE ran properly in the postbuild step, it will have refreshed your MTS package for you.
Now you need to create an ASP that will use your ReturnRs method. Create a new ASP file in Visual Studio. (It does not need to be made part of the project.) Listing 12.6 shows the source code for the ASP.
You need to install the Sheridan grid control on your machine for the code in Listing 12.6 to run properly. As of this writing, the Sheridan grid control is included in the Microsoft RDS Address Book sample.
If you are unable to obtain the Sheridan grid control in the RDS
samples, you can download a trial version of the Sheridan Data
Widgets at http://www.shersoft.com/.
Listing 12.6 ClientTierForMTSAdoRS.ASP Receives
an ADO Recordset from an MTS Component to an ASP
1: <HTML> 2: <HEAD> 3: <TITLE>Client Tier</TITLE> 4: </HEAD> 5: <CENTER> 6: <H1>Browser Client for MTS Component</H1> 7: 8: <OBJECT ID="GRID" WIDTH=600 HEIGHT=200 Datasrc="#ADC" 9: CODEBASE="http://<%=Request.ServerVariables("SERVER_NAME")%> 10: /MSADC/Samples/ssdatb32.cab" 11: CLASSID="CLSID:AC05DC80-7DF1-11d0-839E-00A024A94B3A"> 12: <PARAM NAME="_Version" VALUE="131072"> 13: <PARAM NAME="BackColor" VALUE="-2147483643"> 14: <PARAM NAME="BackColorOdd" VALUE="-2147483643"> 15: <PARAM NAME="ForeColorEven" VALUE="0"> 16: </OBJECT> 17: 18: <OBJECT classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33" 19: ID=ADC HEIGHT=1 WIDTH = 1> 20: </OBJECT> 21: 22: <!-- RDS.DataSpace --><OBJECT ID="ADS1" WIDTH=1 HEIGHT=1 23: CLASSID="CLSID:BD96C556-65A3-11D0-983A-00C04FC29E36"> 24: </OBJECT> 25: 26: <BR> 27: <BR> 28: <INPUT TYPE=BUTTON NAME="ReturnHello" VALUE="Return Hello"> 29: <INPUT NAME=ReturnedHello SIZE=10> 30: <INPUT TYPE=BUTTON NAME="ReturnRS" VALUE="Return RecordSet"> 31: </CENTER> 32: 33: <SCRIPT LANGUAGE= "VBScript"> 34: 35: SUB ReturnHello_OnClick 36: Dim objMyCustomBusinessObject 37: Set objMyCustomBusinessObject = ADS1.CreateObject("MTSComp1.Component1.1", "") 38: ReturnedHello.Value = objMyCustomBusinessObject.ReturnHello 39: Set objMyCustomBusinessObject = Nothing 40: END SUB 41: 42: SUB ReturnRS_OnClick 43: Dim objMyCustomBusinessObject 44: Set objMyCustomBusinessObject = ADS1.CreateObject("MTSComp1.Component1.1", "") 45: ADC.SourceRecordset = objMyCustomBusinessObject.ReturnRs 46: Grid.Refresh 47: Set objMyCustomBusinessObject = Nothing 48: END SUB 49: 50: </SCRIPT> 51: </BODY> 52: </HTML>
Lines 1-6 in Listing 12.6 put up the title for the window and a heading. Lines 8-16 place the Sheridan ActiveX grid control in the page. Lines 18-20 place the Microsoft RDS DataControl (also called the Advanced Data Control or ADC) into the page. Lines 22-24 place the RDS DataSpace object in the page, as you used in Listing 12.4 as well.
Line 30 adds a button called ReturnRS. Lines 42-48 are the code that executes when the user clicks the ReturnRS button. Line 45 sets the ADC Source Recordset to the disconnected Recordset returned by Component1.ReturnRs().
Load this ASP in IE4. Click the ReturnHello button to make sure it still works. Then click the ReturnRs button. If everything runs properly, you should see the records from the Customers table appear in the grid control. It's really cool when it works. When it doesn't work, you need to debug your component.
You can debug your Microsoft Transaction Server component DLL in Visual C++ with the following procedure. Shut down server processes using the Transaction Server Explorer by right-clicking My Computer and selecting Shutdown Server Process.
In your Visual C++ session, under the Project, Settings, Debug, General menu, set the program arguments to the following string: "/p: PackageName"-for example,
/p: "MyFirst"
In the same property sheet, set the executable to the full path of Mtx.EXE-for example,
C:\WINNT\System32\MTx.exe
Set breakpoints in your component DLL in the ReturnHello and ReturnRs functions and run the server process (in the Build menu, select Start Debug and click Go.) Then run IE4 and load ClientTierForMTSAdoRS.ASP. Click the ReturnHello button and/or the ReturnRs button to hit your breakpoints. You should be able to step through the code just like a normal debugging session.
The infrastructure necessary for multitier applications is difficult and time-consuming to build yourself. MTS can do most of that work for you and enable you to concentrate on building your application, not its infrastructure.
MTS components are COM DLLS that you can build with ATL. In your component code, you can create ADO Recordsets from database queries and send the Recordsets to applications on the client tier.
IE4 can host ActiveX controls and, with RDS, can instantiate and communicate over COM, DCOM, and HTTP with MTS components that you build.
What do transactions in MTS do? | |
Some operations that you might perform in your MTS components could involve calling code in several other components or in several databases. MTS transactions enable you to ensure that all that work completes successfully. | |
What is the difference between the RDS DataSpace object I used today and the RDS DataControl object I used yesterday? | |
The DataControl object has a Refresh function that enables you to query a data source behind the Web server. That Refresh function uses the RDS DataFactory object on the middle tier, which could be a security risk in some installations. The DataSpace object enables you to instantiate other components on the middle tier, which you can use instead of the DataFactory object. | |
The RDS DataControl Refresh method provides for asynchronous fetching of ADO Recordsets. Does the DataSpace object provide that capability? | |
No. When retrieving a Recordset from a COM component that was instantiated using the DataSpace object, the Recordset comes across synchronously. In other words, it will block until all the data in the Recordset is fetched. |
The Workshop quiz questions test your understanding of today's material. (The answers appear in Appendix F, "Answers.") The exercises encourage you to apply the information you learned today to real-life situations.
© Copyright, Sams Publishing. All rights reserved.