There are often times when you will want to use server-side functions that just cannot be accomplished through the use of a scripting language, either alone or in combination with the components that ship with Active Server Pages. When you hit this particular wall, you have a few options. First, you can check the market and try to find prebuilt components that satisfy your requirements. Second, you can contract with a third party to build a component for you. Last, and given the time and inclination, my favorite choice: You can build your own component.
When you create your own server components, you have the benefit of using a component built by someone who really understands your business. As you encapsulate line-of-business functions within your components, you can use them on your server and in your client applications or even give them to your clients for use on their systems (for a nominal fee, of course). This chapter focuses on creating server components using the Visual Basic programming language. If you already are familiar with VB, this will be a snap! If you are coming to VB a little later in the game, this chapter will provide you with the skills to begin creating components right away.
Exposing the methods within your classes provides the functionality of your server component.
Learn how an OLE server will become your server component.
Get a step-by-step guide to server component construction in Visual Basic.
There are a number of reasons why Visual Basic is an ideal tool for creating many of the server components that you will use. VB has grabbed the hearts and minds of millions of developers out here in the real world. As you already have learned, VBScript is a subset of Visual Basic. Everything you've learned about VBScript is immediately applicable to development in the VB environment, with a number of useful features not found in its younger sibling.
You will find (or already have found) hundreds of custom controls, such as ActiveX Components, that currently are available for use with Visual Basic. More controls seem to be available each day. The rub is that most of these components require an interface to use them in your development. VB provides a perfect way to wrap the functionality of these third-party components for use in your Active Server Pages development. In addition to the custom controls available in Visual Basic, you also have access to the Win32 API. You can access any number of functions on the system that are impossible to get to using scripting alone. You also can use most any DLL (dynamic-link library) to add additional functionality to the components that you create.
As stated previously, millions of developers have used Visual Basic for a number of years because of its ease of use and flexibility and the speed with which they can develop applications. If you were to hire a new Active Server Pages developer, it is likely that he or she would have VB skills. If not, those skills are just a class or two away.
Another advantage of developing your server components in VB is that there are so many resources available to help you. There are forums on CompuServe, AOL, and Prodigy. The Microsoft Web site provides a wealth of information about VB (a knowledge base, news groups), as well as links to other valuable sites. There are hundreds of quality sites out there dedicated to Visual Basic.
Of course, another benefit of developing your components in Visual Basic is that there are so many excellent sources of information available to help you in your own neighborhood. There are hundreds of texts at your local bookstore with even more information about creating objects in VB than you'll find in this one chapter alone.
Component creation is a process of building objects that you will instantiate in your Active Server Pages scripts. To create objects in Visual Basic, you need to define a blueprint to expose the functions within your object to other applications, in this case ASP. You will use classes to provide this blueprint, or definition and interface, for your objects.
Creating class modules in Visual Basic is the method through which you can develop reusable components. Public procedures within classes are the method through which you can expose functionality from within your class to other objects within your application and to other applications within the system. By creating your functionality within the class framework, you are able to harness the incredible power and ease of use in Visual Basic for your component needs.
As you begin to develop components, consider some of the following details of class development. A class is the description, or blueprint of the object that will be created when the class is instantiated. You can follow some basic guidelines from general object-oriented development to ensure that your class will be a good component candidate.
The idea of data-hiding or encapsulation, is a fundamental principle of object-oriented development. What this means in practice is that access to the data variables within your class should be performed only through member functions of the class. Effectively, you will be hiding the class data from the client program. This ensures that the client application will not unwittingly corrupt the Private object data. This also lets you change the Private members of your class "under the covers"-without changing the Public interface.
Think of your component in terms of properties and methods. The properties are a set of definitions that control attributes of your component. Methods are the actions that the component will take on your behalf. Look to the components that ship with Active Server Pages and Visual Basic and see how the properties and methods work together to provide the components' functionality.
Always strive for limiting the functionality of a component to a basic set. Don't try to put the entire business in a single component. Try to view component creation as an exercise in creating building blocks. If the blocks are too big, you lose flexibility in the design. If they are too small, it will take forever to build the structure.
When the client application instantiates your object (class), there is an opportunity to perform one-time initialization actions for your class. You do this in the Class_Initialize event of the class object. This is a handy place to initialize Private data members and perform any initial error checking, as well as any other functions that need to occur before any of the class methods (functions and/or subroutines) are invoked.
There is also an opportunity to perform actions immediately before the object is destroyed. This processing is performed in the Class_Terminate event of the class. You can destroy any memory that was allocated during the life of the class or perform any other required cleanup.
Once you have defined and coded the class, you can create an instance of the class in Visual Basic by initializing an object variable to that class type:
Dim TestClass as New ClassName
You also can instantiate a class object by using the CreateObject method within your script:
Dim TestClass Set TestClass = Server.CreateObject("Component.ClassName")
The variable TestClass now has a reference to the newly created object. Now you can invoke any of the methods of the newly created object by using the method name with the newly created object reference:
Avalue = TestClass.MethodName(Parm1, Parm2 ParmN)
When you are finished with the object, set the object variable to Nothing to ensure that all memory associated with the object will be released.
Set TestClass = Nothing
When we refer to the methods of a class, we are talking about the set of Public functions and subroutines that reside within the class. Methods are just a convenient way to talk about the procedures available to the user of a component. This is also an easy way to think of these functions, like the methods available to any of the other "objects" within Visual Basic: the form methods, the button methods, and all of the other intrinsic objects in VB that expose their functionality (methods) to you as a developer.
Any subroutine or function that you create in your class module will be visible to the client program if it is declared as Public.
Public Function CalculatePayment(LoanAmount, Term, InterestRate)
When you use the Public identifier in front of the function declaration, the CalculatePayment function is visible to any client that instantiates the class or to any variable that is assigned a reference to it. Private functions within a class are used to provide functions and services that the Public functions can use to fulfill client requests.
In Visual Basic development, as well as HTML development, user interface objects have properties that you set to determine the look and actions of these elements. For example, to access the background color of a text box in the VB environment, you would access its BackColor property:
text1.BackColor = &H00FFFF00&
The use of properties is a simple and intuitive way to access the attributes of an object. You can provide the same functionality to access the properties of your components. This will be a familiar syntax to VB and Active Server Pages developers and will let you use the <object>.<method> notation with any component that you create. Using property procedures also will let you immediately validate the value of the property being set. If invalid data is passed in or a value out of range is provided, the component can respond immediately, instead of waiting for some point downstream in your code to notify the calling program of the error.
Once you have determined what properties you want to provide for your class, you will set aside Private variables to hold these properties. You then need to provide the framework for accessing and updating these class properties.
The Let proedure enables your class users to set the value of a class property under the class's control. Take a quick look at a Let property procedure:
Public Property Let HostName(aHost) If Not Len(aHost) Then Err.Raise vbObjectError + CTRANSACT_ERR_HOSTLEN, "CTransact.Host", _ "Set Host Name: Host Length Invalid" Else m_sHost = aHost End If End Property
You certainly could declare the Private class variable m_sHost (declared as a string) as a Public variable and then let the user set the host name directly. If you did that, you wouldn't have the opportunity to ensure that the host name was valid. Within the Let procedure in the preceding code, in addition to testing for a zero length string, you could also try to ping the host to ensure that it was a valid address or perform additional validations on the passed-in value. When using the Let procedure, you can provide the calling application with immediate feedback as to the status of the component property being set.
In addition to allowing for data validation when the property is set, you also can update other associated variables within your class. If there is another property, say the port to connect to, that is based upon the host that is entered, you can set the port property when the host name is set. This again cannot be performed if you just declared the m_sHost variable as Public. Using the Let procedure can ensure that you don't get any component breaker values in the Private variables of your class. If, for example, a user were to put a NULL value into a variable, it could easily generate a run-time error in another method that references that variable. The rule continues to be "better safe than sorry."
The majority of your coding will set property values of the objects created within the script. Most of you could go the better part of a day without ever requesting the value of an object's property, but there are times when you need to check a property and determine its value. A good example would be the .text property of an entry field.
To provide the "getting" of a property within your class, you create a Property Get procedure. Using the preceding example, you would provide the HostName property procedure to your calling application with the following procedure:
Public Property Get HostName() As Variant HostName = m_sHost End Property
The Let and Get procedures are wonderful for creating properties within your class. Eventually though, you need your class to perform some function in order to become useful. To expose functionality to an external application from within your class, you declare a function or subroutine Public. This enables the application that created an instance of your class to access the procedure.
Any procedure that is not directly used by the client application should be declared Private. This ensures that the client does not inadvertently call a function or subroutine that it should not. Private functions are used to perform internal activities for the component. As you will see in the component example, "Creating Your Server Component," the Public interface usually is the smallest part of the class code. The component interface is intentionally kept small and simple. If the implementation of the method changes (the Private functions), there is no need to change the external (Public) function declaration.
There are two ways you can handle errors within your classes. The first requires you to provide all methods as functions and return a completion code for all Public procedures. This requires a well-defined set of return codes and requires the client program to check for errors in-line after each call to the component returns.
A better way to handle error conditions within your class is to use the VB error-handling framework. If an error occurs within a class module, you will use the Raise method of the Err object to pass notification of the error to the calling program. The error percolates through the procedure levels to a place where an error handler has been registered within the calling program.
You notify the client application of an error by calling the Raise method of the Err object. The syntax for raising an error is shown here:
Err.Raise(Number, Source, Description, HelpFile, HelpContext)
The only required parameter is the Number. When you are creating server components, you usually should include the Source and the Description parameters as well. Say that you are trying to open a local file in a method in your new class. The file cannot be opened because the program cannot find the file on the disk. A run-time error that the component invoked in your code looks something like this:
Err.Raise vbObjectError + 55, "CFileMgr.ReadFile ", "Cannot find file specified"
The constant vbObjectError is a base number to add your component specific error number to. This is the constant that is used when raising run-time errors within an OLE server.
![]()
See "Error Handling in Procedures" for more on error handling within your scripts, in Chapter 9.
Up to now, a number of pages in this chapter have been spent discussing the class module and how it provides the blueprint for your Active Server Pages when your component is invoked. This is well and good, but components live by more than class alone. You need to wrap the class in a suitable way so that you actually can create an instance of it. To do this, you create the class within an OLE server.
There are two types of OLE servers that you can create with Visual Basic. OLE servers can run either out-of-process or in-process. The distinction is basically evident in the server names. An out-of-process server executes in a separate process from the client application that creates an instance of the server (component). An in-process server is packaged as a dynamic-link library and, when instantiated, shares the same process space with the application.
An in-process server has a number of inherent strengths that ensure that it is the type you will build when creating your server components. First, because the server will be running in the same process space as the calling application, the component will be faster, because there is no need to pass data across process boundaries. Second, because the server is packaged as a DLL, if it is already loaded in memory when a new instance is created, there is virtually no load time for the new object.
There are, however, a number of restrictions to keep in mind when creating in-process servers as components in Visual Basic:
You must create the components using Visual Basic Professional or Enterprise Edition, version 4.0 or above. No 16-bit code support is provided.
Because the visible functionality of your object is defined by the Public classes within your DLL, at least one class must be Public.
Any user interface that you would embed into your class will not be visible to the browser requesting information from an Active Server Pages script and potentially could lock the server process requesting the service.
If you package multiple classes in your component, avoid the use of globals or statics in any shared modules. These will only complicate your coding and can lead to incorrect or corrupted memory across multiple instances of any given object.
It's time to begin creating your Active Server Pages component. It's useful to take a moment and discuss what it is, exactly, that your component will do for you. Many of you are working in heterogeneous environments, interacting with PCs, minis, mainframes, and, yes, even UNIX boxes. The component that you are going to build will provide access to a number of transactions living on a multiple host systems.
Here's the scenario: Say that for a number of years, your hypothetical company has been processing TCP/IP requests from external vendors to authorize credit card transactions. You have a well-defined transaction header structure that is used to send a transaction through the system. You want to provide the same authorization transactions from your Web servers.
There are a number of commercial packages out there (even Microsoft's own Internet Control Pack) that provide generic TCP/IP communications. The problem with most of these packages is that they are set up to provide an event when a transaction (or any data, for that matter) is received back from a previous request. By design, they are asynchronous, meaning that the receipt of the response from the transaction occurs some time later. The program or thread execution will not block waiting on a response from the server. This is ideal for interactive client/server applications where you are doing other things while waiting for a response (or multiple responses). Due to the stateless nature of connections on the Internet, there is no facility to continue to process the script and then respond to this "transaction complete" event later.
So what you are going to build is a TCP/IP transaction class that lets you call a method that sends a transaction to your host system and waits for a response before returning from the procedure call. All formatting of the transaction takes place within the class, as well as all of the communications details. The only things that the script must provide are a host name and port to connect to, an account to validate, and an amount to authorize. We're sure you can think of a number of situations in which you would want to leverage TCP/IP transactions from your servers. This component can easily be modified to accept different header and transaction types. Out-of-the-box, it should give you a good sense of the steps involved in building your own server components.
The first thing you need to do is ensure that you have a copy of the 32-bit version of Visual Basic, either the Professional or Enterprise Edition. Then open up a new project to begin creation of your transaction component.
It's that easy! Now you are ready to begin.
When you start a new project in Visual Basic, there are a number of custom controls and object references that get added to your project for free, or they seem so at the time. In fact, they are not free at all. If you open the file AUTO32LD.vbp (found in the directory where the VB32.exe lives on your system) in Notepad, you see the default objects that are loaded each time you request a new project. To permanently change these defaults, you can edit this file to remove the object references.
Any component or reference that you do not use but do retain in your project when it is built, tags along for the ride, even though it never is invoked. This adds overhead to your application and swells the number of disks required to distribute your new component.
So now you will remove all the custom controls that you can from your newly created default project to reduce this overhead:
The first step brings up a dialog box with all the controls available for your use, shown in Figure 14.1, as well as those currently active in your project. Those that are active will have the check box checked. Remove all the currently selected objects by removing the check in any checked box. Then click the OK button to save your changes.
Lighten up the application by removing unused controls.
The second step brings up a dialog box with all the references available for your use, in addition to those currently in use for the project. If you are not going to be performing any database functions using the Jet database engine, remove the reference to the DAO library. This cuts your distribution down by over a meg. If there are any other references that you will not need, remove them as well by unchecking the checked boxes.
There are no user interface attributes in your server component, so remove the default form that loaded in your new project. To remove Form1, as outlined in step three, select the form from the project window. If the project window is not visible, press Ctrl+R to show it.
While all functionality that you will expose to your client application-in this case, an Active Server Pages script-is through the Public methods of a class, there still is component-level initialization that can take place when the component is created. Component-level initialization takes place in the Sub Main procedure of the component.
To add the Sub Main procedure to your new project, you need to add a code module. To do so, follow these steps:
These steps add a new module to the project. The module defaults to Module1 in the project window. You are creating a component that will provide transaction processing for your server, and this component will be called CTransact, so you will call the module with Sub Main in it CTransact_Main.
You now have created an empty Sub Main procedure in the CTransact_Main module:
Sub Main() End Sub
Because you will perform no component-level initialization in your transaction component, you can leave the Sub Main procedure empty. Even though no explicit initialization takes place, you must have a Sub Main procedure defined in your component, or you will receive an error when you try to instantiate the component.
Now is a good time to save your project. Before you do that, though, set up the project so that the compiler creates an OLE in-process server instead of a normal Windows executable program. The settings regarding the code generation are found on the Options dialog box. To get there, follow these steps:
![]()
The menu options for Visual Basic 4.0 specified in the steps defined within this chapter may change in version 5.0.
As shown in Figure 14.2, you will notice a number of user-definable options for the project. The first thing you need to do is specify the Project Name. The Project Name will be the name you will use when specifying an object to create within your Active Server Pages script.
The StartMode setting specifies whether you will build a normal Windows executable file or an OLE server. Of course, you want to build an OLE server.
The last field that you will edit on the project is the Application Description entry field. This will be the text that you will see when you browse the objects and components currently available on a system. You want to provide a short and concise description of your component.
The Project tab defines your component naming.
You will be prompted to save the CTransact_Main.bas module in the default directory. Accept the default directory or create a new directory from the Save As dialog box, and then save the module. When prompted for the project name to save, replace the Project1 name with CTransact.
Congratulations! You have just completed the first task in creating your component.
Now the real work begins. Up to now, you have been dealing with the administration of creating a server component, setting up the project, selecting project options, and creating a main procedure. Now you begin to develop the class data and methods that will perform the transactions that you have been working toward.
The first step is, of course, to create the class module. The class information will live in its own class code module, a file with a .CLS extension. To create the class module, select Insert, Class Module from the main menu.
Now you've added a new class module to your project. If the class module is not currently selected, select the module from the project window by double-clicking the name Class1 or by selecting Class1 and then clicking the View Code button of the project window. Remember that if your project window is not visible, you can pop it up using Ctrl+R.
In the next few steps, you will rename your class, set it to be Public, and select the instancing options. With the class code window active, press the F4 key to bring up the class properties window. It will look like the one shown in Figure 14.3.
Editing class properties is just like changing any VB object's properties.
The first of the preceding steps, changing the name, is important, because the name of the class is referenced when you create an instance of your component. Just as you take care in naming your class methods, you need to make a good choice for the class name.
The Public property determines the visibility of the class outside your OLE server. If you were to leave the class private, no one could access any of the transaction functions that you will build into the class shortly.
The last property of your class that you set was the Instancing property. This property determines how the OLE server is managed when it is instantiated by a client application. You selected the Creatable Multi-Use property, which enables the object, if already created, to be supplied by the existing object. If no object currently exists, one will be created. Contrast this to the Creatable Single Use option, where each CreateObject request causes a separate copy of the OLE server to be started. The Creatable Multi-Use option uses memory more efficiently and is the primary choice for externally created components like the one you are building.
To perform TCP/IP transactions, you will be using the Windows Sockets library calls in this section. One of the nice things about Visual Basic is that you can declare just about any dynamic-link library and use it in the VB environment. To use these WinSock functions within your component, you need to declare them to Visual Basic. This is done with the declare statements shown in Listing 14.1, in the general code of the Transaction class.
Listing 14.1 TRANSACTION.CLS-Declaring the WinSock Functions to Visual Basic
'Winsock calls in VB format, for 32 bit, WSOCK32.DLL Private Declare Function bind Lib "wsock32.dll" (ByVal s As Long, _ addr As sockaddr_type, _ ByVal namelen As Long) As Long Private Declare Function inet_addr Lib "wsock32.dll" (ByVal s _ As String) As Long Private Declare Function gethostbyname Lib "wsock32.dll" _ (ByVal hostname As String) As Long
There are a number of other Win32 API functions that you will be using in your class. You can find all the declarations for these functions in the TRANSACTION.CLS module, in the declarations section.
There is also a number of helper procedures within the class that handle the dirty work of the TCP/IP communications. Generally, the flow for the transactions follows this path:
Each of the preceding steps has a Private procedure that handles one part of the communications chore. Listing 14.2 shows the code for the fn_Connect function, which connects to the host system. All the helper procedures do one activity and return a Boolean value if the activity is completed successfully.
Listing 14.2 TRANSACTION.CLS-The Connect Function
Private Function fn_Connect(iSocket As Long, lAddress As Long, iPort As Integer) As Boolean Dim sockaddr As sockaddr_type Dim rc As Integer sockaddr.sin_family = AF_INET sockaddr.sin_port = htons(iPort) sockaddr.sin_addr = lAddress sockaddr.sin_zero = " " rc = connect(iSocket, sockaddr, Len(sockaddr)) If rc = SOCKET_ERROR Then Err.Raise vbObjectError + CTRANSACT_ERR_CONNECT, "Transaction.fn_InitSockets", fn_GetLastSockError() Exit Function End If fn_Connect = True End Function
If the connection fails, an error is raised, passing back the error code as well as the text describing the reason for the connection failure. The fn_GetLastSockError function returns the error text for the most recent socket error, utilizing a call to the Windows Sockets extension function WSAGetLastError(). For additional information about the WinSock 1.1 specification, check out the Microsoft Web site at http://www.microsoft.com.
The first thing you need to do after setting up the declarations for WinSock and Win32 API functions is to create your Private class variables, as shown in Listing 14.3. They are dimensioned as Private so that your client does not have access to them, and cannot accidentally change one of their values at an inopportune time.
Listing 14.3 TRANSACTION.CLS-Declaring Class Variables Private
'Module Level Variable Declarations Private msHost As String ' Holds the host name Private mlHost As Long ' 32 bit host address Private miPort As Integer ' Host port to connect to Private miConnectTimeout As Integer ' connect timeout Private miReceiveTimeout As Integer ' receive timeout Private msBuffer As String ' buffer to hold transmitted data Private mbInProcess As Boolean ' in process flag
It is great that you have all these Private variables to use in your class, but you also need to get some pertinent information from the component user so you can send your transaction on to the correct host. In the section "Using Property Procedures", we talked about property procedures that enable your users to set properties within your component. Two properties that must be set for you to perform your transaction are the Host and Port properties. These will provide you with the name of the host to send the transaction to, as well as the port to which the host will be listening. The property procedure for the Host property is shown in Listing 14.4.
Listing 14.4 TRANSACTION.CLS-Property Procedures Enforce Data Hiding
Public Property Let Host(aHost As String) If Len(aHost) = 0 Then Err.Raise CTRANSACT_ERR_HOSTLEN, "Transaction.Host", "Set Host Name: Host Length Invalid" Exit Property End If mlHost = fn_GetHostByName(aHost) If mlHost = 0 Then Err.Raise CTRANSACT_ERR_HOSTBYNAME, "Transaction.Host", fn_GetLastSockError() Exit Property End If msHost = aHost End Property
Notice in the code in Listing 14.4 that you are handling errors by raising run-time errors that your component user will catch in his or her code. If the Host length sent in by the application is zero, you will raise an error. Also, if the host name cannot be resolved to a physical address (the fn_GetHostByName function provides the name resolution services), an error is also raised. If you were setting properties just by allowing your component user access to a Public variable sHost, you would have to check for a valid host name each time you performed a transaction. By handling this check through the use of a property procedure, you can raise the error if needed immediately when the property is set.
There is only one Public procedure in the CTransact class. This is the procedure that initiates the transaction and returns a completion code to the calling program. The function must be declared as Public so it will be visible from outside the component.
The first part of the method (or procedure) is the declaration. Parameters are passed to the method and a return code of type Long will be sent back when the transaction is completed.
Public Function Transact(transaction As Integer, Version As Integer, _ inBuffer As String) As Long
There are a number of authorization transactions that can be processed by the back-end service. Also, to support new versions of transactions as time goes on, a version is passed in as a parameter as well. This is a handy way to enhance transactions while not having to change any of the clients currently using a previous version. The buffer that is passed in is the authorization string formatted by the Active Server Pages script.
The first part of the code, found in Listing 14.5, ensures that a valid Host property has been set, as well as a value for the Port property.
Listing 14.5 TRANSACTION.CLS-Verifying that Required Class Properties Have Been Set
If miPort = 0 Then Err.Raise CTRANSACT_ERR_PORTNOTSET, "CTransact.Transact", _ "Port must be set prior to invoking Transaction method" Exit Function End If If mlHost = 0 Then Err.Raise CTRANSACT_ERR_HOSTNOTSET, "CTransact.Transact", _ "Port must be set prior to invoking Transaction method" Exit Function End If End If
You check for a valid host when the property is set, but you must also ensure that the property has been set before you begin the transaction. You could have added another Private variable as a flag, say blnHostSet, but it is just as easy to check the value of the miPort and mlHost variables. It also saves a bit of memory (the less module-level variables you use, the better).
The interaction with the host system is through a standard header that is defined by the type TRAN_HDR. All of the transactions are performed using this header, and the return code from the transaction will be put in the ReturnCode member of the TRAN_HDR type. Ideally, you would send the structure in the transaction. In the C or C++ programming languages, you would just cast the structure variable as a char * or a void * and be done with it. In Visual Basic, you need to send the transaction as a string. To do this, we ended up creating a little C DLL to perform the conversion from a string to a type and back again.
ON THE WEB |
http://www.mcp.com/que/aspAt this book's Web site, you'll find the source code for VBUTIL.DLL and the project files to re-create it using Microsoft's Visual C++ product. |
The next part of the code fills in the TRAN_HDR type, converts it to a structure, and prepares to send the transaction over the wire (see Listing 14.6).
Listing 14.6 TRANSACTION.CLS-Using the VBUTIL CopyStructToString Function to Simplify the String Conversion
hdr.PacketNumber = Format$(transaction, "00000000") hdr.Version = Format$(Version, "0000") hdr.ReturnCode = "9999" hdr.OperatorNumber = "" hdr.RecordLength = Len(hdr) + Len(inBuffer) msg = Space$(Len(hdr)) rc = CopyStructToString(hdr, msg, Len(hdr))
Notice that you must pre-allocate the msg (which will host the string representation of the TRAN_HDR type) string by filling it with spaces equal to the size of the header into which you are going to copy it. If you forget to pre-allocate the msg string, you receive an error.
Now comes the heart of the communications functions. All the TCP/IP functions have been created for you as Private procedures in the class, and calling them within the Transact method is shown in Listing 14.7. As each function is called, the function returns codes that are checked after the call is made to ensure that you are still communicating with the host system.
Listing 14.7 TRANSACTION.CLS-The Bulk of the Communication Is Transparent to the Transact Method
socket = fn_OpenSocket() If socket = 0 Then Err.Raise CTRANSACT_ERR_OPENSOCKET, "CTransact.Transact", fn_GetLastSockError() Exit Function End If If fn_Connect(socket, mlHost, miPort) Then If fn_SendData(socket, msg & inBuffer) Then msBuffer = fn_ReceiveData(socket, 60) If Len(msBuffer) Then rc = fn_CloseSocket(socket) rc = CopyStructToString(hdr, msBuffer, Len(hdr)) retCode = hdr.ReturnCode ' remove rich header from data buffer msBuffer = Mid$(msBuffer, Len(hdr) + 1) Else Exit Function End If Else rc = fn_CloseSocket(socket) Err.Raise CTRANSACT_ERR_SEND, "CTransact.Transact", fn_GetLastSockError() End If Else rc = fn_CloseSocket(socket) End If
There are a couple of return code type variables used in the Transact method. The first, retCode, holds the value of the returned code from the host transaction. This is a four-character string variable. The second, rc, is an integer that holds the transaction specific return code that is sent back to the calling program. Once you receive the return code (retCode) from the host, you interrogate it, as shown in Listing 14.8, to return the appropriate value to the calling application.
Listing 14.8 TRANSACTION.CLS-Formatting the Return Code for Your Calling Application
'Based upon the return code from the transaction, pass a value 'to the calling app Select Case Left$(retCode, 2) Case "00" rc = TRANSACT_RC_SUCCESS Case "IL" rc = TRANSACT_RC_INVALID_ACCOUNT Case "IR" rc = TRANSACT_RC_INVALID_TRAN Case Else rc = TRANSACT_RC_SERVER_ERROR End Select Transact = rc
The last step in building your transaction component is to generate the DLL. In the secion "The Project Dialog Box," you set the project options to generate an OLE server. Now you just need to instruct the compiler to generate an OLE DLL:
That's it! You now have successfully built your first Active Server component. Of course, you can use this component with Visual Basic, Access, Excel, or any other application that supports OLE components. The new component was registered automatically for you on the system where it was initially created.
Now that you have built the component, you need to create the form and Active Server Pages script to invoke that component. If you are going to use the component on a machine other than the one you created it on, you need to register the component on that machine.
When you create the OLE DLL file in the Visual Basic environment, the component is registered automatically on the machine where it is compiled. If you want to move the DLL and associated support files to another machine, you need to register the new control after you place it on the system. There are a couple of ways to do this. If you are going to distribute your component, you can create an installation program using the VB Setup Wizard or another third-party installation package. The control will be registered during the installation process.
You also can just move the files to the new machine and register the control using the REGSVR32.EXE program that ships with Visual Basic. You can find the registration application on the Visual Basic CD-ROM in the \TOOLS\PSS directory. Here's how to register the control using the REGSVR32 program:
The easiest way to initially test your new component is to start another instance of Visual Basic, create a simple form, and create an instance of the component. Then you can set the properties and call the Transact method. This lets you fine-tune the component before you try to integrate it into your Active Server Pages scripts.
To test the component, start another instance of Visual Basic. Add a command button to the Form1 that's displayed at startup. Double-click the newly created command button to bring up the code window. Now type the following code found in Listing 14.9 into the code window to test your new component.
Listing 14.9 form1.frm-Test Script for the New Component
Private Sub Command1_Click() Dim tran As New Transaction On Error GoTo COMMAND_ERR tran.Host = "localhost" tran.Port = 7511 rc = tran.Transact(1000, 0, "TEST TRAN") MsgBox rc Exit_Sub: Exit Sub COMMAND_ERR: MsgBox "Error: " & Err.Number & " " & Err.Description Resume Exit_Sub End Sub
The only action left to take before testing your component is to add a reference to it in your new project:
While testing, it's helpful to force errors to ensure that error handling within the component is functioning correctly. For example, you can comment out the line tran.Host = "localhost". This generates an error because, as you may remember from the HostName property procedure code found in the section "The Let Procedure", the host must be set prior to calling the Transact function.
![]()
See "Checking the Routine" for more information about methods of testing, in Chapter 9.
The error will be raised in the component and caught in the calling app by this directive:
On Error GoTo COMMAND_ERR
In your Active Server Pages development, you will not be popping up message boxes on your server, but at a minimum, you surely will want to log any communication errors for further study and return an appropriate response to your client.
To test the component on your server, you need to build a simple form that calls an Active Server script and passes the appropriate parameters to it (account number, authorization amount). Then you can create your transaction object, call the Transact method, and return the results to your client.
ON THE WEB |
http://www.mcp.com/que/aspThe HTML form code and the Active Server Pages script to process the authorization request can be found on this book's Web site. (The assumption is that you might not want to read through another bare-bones HTML forms lecture at this juncture.) All of the Visual Basic code for the component, as well as the CTRANSACT.DLL, are on the site. We also included the source code and DLL for the VBUTIL.DLL functions. |
We finished the section on working with Active Server Pages objects and components with this chapter on building your own components. You have walked through the steps to generate your own components in Visual Basic. You now have all the tools and knowledge to create an infinite variety of applications for your Internet/intranet site using custom components. In Part IV, you will focus on database management and providing active content with live links to your back-end database systems.
Here's what to look forward to in Part IV:
© 1997, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.