Visual C++ 4.2 Dramatically Reduces the Learning Curve for Writing Internet Apps
Nancy Nicolaisen is a freelance author. She works from her home in Anchorage, Alaska.
With ActiveXª, the technology you learned as far back as last week is already obsolete. Keeping up with the products spewing forth from the giant Microsoft Technology Butter Churn is proving to be a Herculean task. Microsoft¨ Visual C++¨ 4.2 supports two tubs of new classes, one for server-side Internet programming and one for the client side.
The server-side tub provides the components necessary to write ISAPI programs. This includes the ISAPI server skeleton, as well as ISAPI extensions, filters, streams, and contexts. The client-side tub includes all of the pieces for writing complete browser applications, augmented with file transfers and TCP/IP protocol handlers.
With these two tubs of classes, along with ActiveX doc objects, MFC programmers can grease up that bumpy ActiveX ride. I will also discuss some of the other new features in Visual C++ 4.2, such as the new ActiveX controls and the Template Graphics 3D tools, both of which are great for making those creamery-fresh Web pages.
Predictably enough, Visual C++ server API-based applications target the Windows NT¨ operating system. Collectively called ISAPI, this group of objects and development tool extensions was originally unveiled as part of the MFC 4.2 prerelease at the spring 1996 Microsoft Internet Developers Conference. The ISAPI technology consists of five new MFC classes (see Figure 1) for creating DLLs that enrich Internet servers and Web pages with interactivity, visual appeal, and intelligent message-filtering capability.
The CHttpServer class provides a framework for the basic server application object. It's essentially a wrapper for ISAPI and can process all types of client requests, including those for Common Gateway Interface executables and Internet server applications packaged in DLLs.
The CHttpServerContext class manages the HTTP server's request traffic in a nonblocking fashion. When the HTTP server receives a request from a client browser, a CHttpServerContext object is created for each call into the server. These objects let CHttpServer service each client's requests in a different thread, improving performance and simplifying server application logic. It also allows multiple simultaneous connections to the CHttpServer object by different clients. The CHttpServer can communicate directly with the client or use the CHttpServerContext object as a conduit for replies.
The mechanics of communication between client and server using the new ISAPI MFC classes may be surprisingly familiar to you. When a CHttpServer object receives a client command, it refers to parse maps that associate the inbound command to the appropriate class member and assigns values to function parameters. All you have to do is override the client commands you want to process as you would in any MFC program.
Filtering the Client Command Stream
HTTP filters are replaceable DLLs called by the server upon receipt of an HTTP client request. The filter tells the server at load time which requests it wants to process. Filters essentially behave like hook functions for the event categories they process, receiving the first opportunity to process the events they specified to the server when loading. Filters can process events, throw them away, or pass them on to the server object for processing.
If the Internet is going to be a useful business tool, it needs effective filtering on both the server and client ends of a connection. The ISAPI class CHttpFilter is a simple mechanism for inserting a filter in the message stream. CHttpFilter objects allow you to implement protections such as custom authentication schemes, compression, encryption, logging, and traffic analysis. Filter applications insert themselves between network connections and HTTP servers. A single filter may intercede in a number of server tasks, such as reading raw data from the client or actually taking over part of the processing for an HTTP request.
You can have multiple filters installed on a server object. Filters can register their priority at load time to determine their rank in the server's notification order. In the absence of priority levels, filters receive notification of inbound events based on load order. This is particularly powerful in view of the fact that the ISAPI family of server objects offer support for protocols other than HTTP, such as CGI.
Because of the multithreaded nature of the CHttpServer class, filters, like servers, need to be able to figure out the context of the requests on which they operate. The CHttpFilterContext object does this. Like the Server object, a single copy of a given filter object exists for each server instance. The CHttpFilterContext object referees access to the filter by segregating requests according to client and thread, enabling concurrent client calls to the CHttpFilter object.
You Want Me to Parse What?
OK, say you've got some great material you want to publish on the net. But by the time it gets to the users, it had better be in HTML format or the browser is going to show them a lot of ugly little gray windows. The problem of formatting data for download to users is made more complicated by the fact that you can count on both the HTML spec and prevailing browser architectures having the lifespan of a radioactive mayfly.
ISAPI goes a long way toward solving this problem with the CHtmlStream class. CHtmlStream manages in-memory HTML data, allowing you to send the information across a client connection without having to worry about much other than initial format translations from the data store into the HTML memory object. CHtmlStream behaves in much the same fashion as the MFC class CFile, with many of the same members and attributes, but the two are not derived from the same base class. One important difference is that the data in a CHtmlStream is temporarily buffered by the system until it is sent out, and this buffered data can't be read by applications. The system manages the staging and transmission of the buffered data, concealing TCP/IP negotiation from the app.
In general, you don't have to construct the CHtmlStream objects yourself.
Instead, just call CHttpServer::ConstructStream, which passes a CHtmlStream back to your app. If you want to implement special handling of the data you can override this function. CHtmlStream objects have a flexible memory allocation scheme. You can allow them to automatically size and allocate their own memory or attach an existing allocation object to the CHtmlStream object with a call to the Attach member. No matter which method you use, if the data outgrows the space, additional memory is allocated in chunks sized by a parameter to the object's constructor called nGrowBytes (a parameter you pass to CHttpServer::ConstructStream). The CHtmlStream destructor automatically cleans up the allocations associated with the object when it goes out of scope, but if you use the Attach gambit to provide a buffer, you have to make sure to do the deallocation yourself.
Hands On With ISAPI
ISAPI classes support a type of application called server extensions. Server extensions are DLLs that implement HTTP services, and are loaded and called by an HTTP server at run time. Server extensions allow you to design and code the functionality specific to your data without having to create the basic socket-handling level of HTTP service.
Extensions are not servers-they are components that plug into servers. (You may also hear them referred to as extensions, Internet Server Applications (ISAs), and ActiveX Server extensions.) Server extensions are useful because they allow developers with little or no communication programming experience to write a task-specific ISA. They also let you optimize existing servers by providing fast, application-specific logic for retrieving and formatting specialized data.
The WWWQUOTE sample included with Visual C++ 4.2 demonstrates sophisticated features of the ISAPI classes such as multithreading and data-on-demand publishing. Let's start with a look at how it matches HTTP client requests with the appropriate handler.
The mechanism that links incoming client requests for HTTP data to the server app is called the parse map. It is similar to the message-mapping macros used by MFC. However, unlike the message-map macros, you have to write the ISA parse map (see Figure 2).
Here's how the parse maps route client requests. Let's assume that on the client end there is a forms-based UI that collects information from the user and formats a request to the server that includes the URL for your ISAPI DLL. In addition, this request contains a list of the necessary function parameters assembled from information the user entered in the form.
In creating the ISA, your job is fairly easy because you don't have to do anything about parsing the function name or parameters out of the inbound request (except writing the parse map). You simply write the member function and declare its parameters in the ordinary fashion. The parse-map macros intercept the function parameters and any command-line arguments, including both optional and default arguments and parameters. When the URL awakens your ISA, MFC calls your function, passing it the appropriate parameters.
Writing Your Own ISAs
If you're not completely clear on what files you need to write an ISA, the Visual C++4.2 AppWizard includes an ISA target, so it's just a matter of point and click. Choose the option "Generate a server extension object" from the New Project Work Space dialog, provide a name for the ISA, and click Finish to create an ISA skeleton project.
For each HTTP command you wish to process, you'll need to write a handler function and add an entry to the parse map. This is a fairly straightforward job because you can look at the Extension Control Block (ECB) structure defined in MFC 4.2, which organizes all of the data that travels between your app and the client (see Figure 3). There are two things to note in Figure 3. First, nowhere in the ECB are there any references to specific connections, clients, or protocols. You could just as easily be writing a standalone desktop data retrieval system as an Internet publishing component. Second, there is no limitation on the sort of data you can send out the pipeline. As long as you can format your data store for display in the browser, you're good to go. This last feature is in marked contrast to many of the proprietary server development tools being introduced.
To complete your app, you'll simply have to add whatever member functions are necessary to locate and retrieve the local data the client requested. In the case of WWWQUOTE, ODBC calls are used to set up a query to a high volume SQL server. Stock quote records are retrieved based on user input and price/volume information is formatted into an HTML document that, in turn, is shipped off to the client.
WWWQUOTE is fully thread-capable, using the CHttpContext objects to mediate simultaneous connections. It's important to note that not all of the database access technologies around are appropriate for use in a multithreaded ISA. Steer clear of any tool that uses the Microsoft Jet 3.0 database engine or the COM-based Data Access Objects (DAO) API-they're not threadsafe. Instead, use Microsoft SQL Serverª 6.x or the OLE DB interface (for more information, see "Talk to Any Database the COM Way Using the OLE DB Interface," MSJ, July 1996). Beginning with this version of Visual C++, you can also roll your own database access with fully threadsafe MFC ODBC classes.
WinInet: Client-Side Classes
Microsoft's Visual C++ language group hasn't been spending all of its time thinking up ways to light up the server marketplace. Another new family of MFC classes, WinInet, puts powerful tools in the hands of developers targeting the browser side of the equation (see Figure 4).
WinInet encapsulates a trio of the most popular Internet communication protocols: HTTP, FTP, and Gopher. Like ISAPI on the server side, WinInet lets developers code Internet client applications at a relatively high level. All protocol handling is masked by WinInet, with each of the three protocols packaged in objects that use a mutually consistent set of functions. The WinInet classes are actually implemented as wrappers for the Win32¨ WinInet family of functions, so the technology has seen some deployment before its appearance in MFC.
Writing a browser is somewhat more formidable than writing a server because there are unpredictable elements on both sides of your application. On the front end, you have to deal with all of the potential human interface difficulties. On the back end, you must initiate, negotiate, and manage a network connection. WinInet gives you a great deal of leverage on the back end of the browser by allowing you to treat the network connection as you would an ordinary MFC CStdioFile. In particular, you can expect to be completely insulated from socket programming. WinInet classes inject a valuable degree of robustness into custom browsers by handling the buffering of I/O, providing typesafe handles to data, inserting default parameters for many functions, processing exceptions for common Internet errors, and cleaning up open handles and net connections.
Every Internet client begins its life when a session is created. Class CInternetSession implements Internet sessions as objects. Applications can use one or more CInternetSession objects simultaneously, allowing browsers to take advantage of multiple physical connections if they exist. To connect a WinInet-based browser to a server, you need to spawn a connection using one of the CInternetSession member functions: GetFtpConnection, GetHttpConnection, or GetGopherConnection. These members create a connection and return a pointer to an appropriately typed Connection object, which manages the initiation, negotiation, and maintenance of the server connection.
With some limitations, you can directly manipulate files on the server using the CInternetFile object. The most streamlined way to do this is with the CInternetSession::OpenURL function. Simply pass the target file's URL as a parameter to OpenURL. It parses the URL, opens the connection to the server, and returns a CInternetFile object. OpenURL is not protocol-specific; the same code works for any FTP, HTTP, Gopher, or local URL. If the URL you pass specifies a local file, OpenURL returns a CStdioFile rather than a CInternetFile.
If you find yourself working on a WinInet browser, check out the table in the Visual C++ 4.2 docs titled "Operation Prerequisites for Internet Client Classes." This table basically lays out the script clients and servers follow when they conduct an Internet session conversation. It shows the order in which to create and use the WinInet objects as well as the sequence of member function calls for common operations like connecting to a server; opening, reading, and writing files; and changing directories.
Template Graphics 3D Tools
Visual C++ 4.2 provides excellent tools for 3D rendering and animation for developers who want to exploit the interactive potential of the net. The TGS Open Inventor is a powerful C++ class library that grew out of virtual reality rendering research at Silicon Graphics. Open Inventor raises the level of the 3D programming model by wrapping the data management, user interaction, and rendering processes in its set of virtual reality objects.
Perhaps the best feature of the Open Inventor objects is that you can work at many levels of detail. You can write applications that manipulate granular aspects of VR rendering and animation using constructs like Light, Camera, and Sphere. Or, if you prefer to deal with scene geometry and rendering at a higher level, Open Inventor provides very high-level objects such as a Walkthrough viewer.
Open Inventor uses a scene database in which it stores descriptions of all of the solid volumes, light sources, and materials the application uses to construct virtual reality landscapes. This data is hierarchically organized in 3D scene graphs. Scene graph data structures allow applications to apply global effects-like changes in lighting or orientation-to the virtual landscape very flexibly and efficiently. Scene graphs let developers implement picking, the interactive operation by which users select and manipulate objects within the VR landscape. Open Inventor's picking mechanism is extremely precise and flexible.
The most compelling thing about the Open Inventor technology from a developer's point of view is that it supports the VRML file format for passing scene geometry data over the Internet. VRML provides for a very compact encoding of scene geometries, and uses only ASCII data. It can define either specific objects or complete scenes, and is ideally suited for asynchronous transmission over the low bandwidth connections characteristic of dialup Internet sessions. The great strength of VRML is that it delegates the actual construction and rendering of scenes to the platform on the client side of an Internet conversation. Since VRML descriptions are fairly sparse, this has the twin benefits of allowing the construction of rich, complex VR scenes with a minimum of data transmission, and improving rendering performance by allowing a VRML-enabled browser to intelligently exploit local graphics hardware.
Open Inventor was originally based on a portable core component coupled with a Motif-specific component called SoXt. Visual C++ 4.2 includes a version of Open Inventor developed by Template Graphics Software under a licensing agreement with Microsoft. The Win32 platform-specific element is called SoWin and retains a structure similar to its UNIX predecessor. It contains the same classes as SoXt, though in some cases their methods differ in the data types of the parameters. This symmetry should allow existing UNIX Open Inventor code to port relatively easily to Windows¨.
Visual C++ packages the Open Inventor technology in its IVF class library. The integration between existing MFC classes and IVF is strikingly clean and elegant. In IVF applications, the application, document, main frame window, and view objects simply take on additional functionality to support 3D visualization. To get started with 3D applications, simply use the IVF AppWizard to generate a skeleton MFC application enabled for 3D graphics and, optionally, for 3D scene viewers. There are some extra licensing issues for distributing Open Inventor; be sure to read the documentation supplied with it.
New ActiveX Controls
OLE custom controls have a new name now: ActiveX controls. Visual C++ 4.2 includes a set of eleven useful ActiveX controls, some of which contain some of what is clearly best-of-breed functionality. LEADTOOLS' imaging OCX provides over 150 functions for filtering, translating, and manipulating images. Blue Sky's Smart Help control allows users to customize and expand application help facilities. Visual Voice for TAPI Solo gives developers a huge leg up on touch tone data access and voice mail applications. The remaining selection provides for user data modeling, diagramming, user-defined gauges, a calendar manager control, and MPEG playback.
Certainly the guest of honor at the controls party is the TGS Visual 3Space control. Visual 3Space is a complete 3D browser component that supports both VRML 1.0 and Open Inventor 2.x virtual reality scene data formats. It essentially provides a stable and flexible generic 3D browser object right out of the can.
As either a browser or a viewer object, the Visual 3Space control accepts data created from any VRML-compliant drafting or drawing tool, including PTC Pro/Engineer, SDRC Ideas, and Autodesk 3DStudio. It's also possible to synthesize your own data for the Visual 3Space control by using the Open Inventor SDK from Silicon Graphics or Template Graphics Software. The Visual 3Space control is fairly flexible. It supports the DDE interface used by Microsoft and Netscape. It also supports OCX container applications, including Word, Microsoft Excel, Microsoft Access, and PowerPoint¨.
What's In a Moniker
Web developers have been getting away with murder on the performance front for quite a while now. This situation is not going to last forever. It's increasingly necessary to be concerned with browser performance. The new MFC class CAsyncMonikerFile allows Internet-enabled applications and ActiveX controls to do "lazy loading" of images or other voluminous data. This makes it fairly easy to add nonblocking data-transfer operations to browsers or ActiveX plug-ins.
The CAsyncMonikerFile class implements the OLE IMoniker interface and allows you to connect the moniker to a URL. IMoniker basically makes the object to which it belongs both visible and accessible throughout the distributed system in which it lives. This is obviously useful for browsers downloading big chunks of data, but it is a critical enabling technology for collaboration among distributed objects as well. For example, the MFC class CDataPathProperty implements ActiveX control properties that can be loaded asynchronously. Asynchronous ActiveX controls can use container callback functions; they test the container to see if the container has more data to supply and gracefully yield when the container is not able to participate in the transfer of data.
The concept of monikers is worth a few words here. Monikers are fundamental to linking in OLE because a linked object must contain a moniker identifying its source of data; that is, the actual object the link points to. When a user activates the linked object, the moniker is bound; binding loads the link's source data into memory, the data is displayed, and the user is able to modify the source object's data. One of the reasons OLE hasn't met with frenzied adoption among desktop developers is that linking through monikers is an awful lot of work for the result you see on a standalone system. But, when you're talking about distributed objects, this stuff is worth it. Not only does it make your life easier, unlike desktop OLE, it has the potential to phenomenally improve performance on the client end because data transfers between the objects are nonblocking. So, even if you don't care about OLE, the asynchronous moniker concept is very useful if you are creating a custom browser.
ActiveX Document Servers
If you aren't ready to dive in and retrofit old code for the Internet, that's OK. It's a good idea, though, to take out a bit of cheap insurance by using the new tools in Visual C++ 4.2 to insert support for key net technologies in new applications. In particular, consider using the AppWizard tools to make your desktop apps ActiveX document object servers.
The centerpiece of the ActiveX document object server is the new doc objects technology. Doc objects are a set of extensions to OLE's compound document construct. They can manage their own views even though they are captive inside an OLE container. Doc objects control their display and printing functions, maintaining a private copy of a data set that defines their state and appearance. Because they have access to storage for their data set, the doc object can create and manage one or more views of its data. Doc objects can support a variety of data views. In the case of a spreadsheet, the document might display itself as both a table and a graph. The document's views behave as filters through which the data can be seen. In general, the doc object implements both screen and printer versions for each of its views.
Doc objects can communicate with container apps through the IOleDocument interface. The container can query the interface, find out what views the doc object can display, and ask the document to display itself. Doc objects are able to live and interact outside OLE containers as well. Every doc object couples a view frame provider with the IOleDocument interface; when the doc object is embedded in a container, the container provides the view frame, and when the document is not embedded in a container, the doc object server provides the view frame.
It may sound a bit formidable, but for new applications doc object support is as easy as a click of the mouse in AppWizard. When you select application options, just check the boxes shown in Figure 5.
Figure 5 ActiveX doc objects in Visual C++ 4.2
Selecting the doc object support in AppWizard doesn't necessarily mean that you have to implement it right away, but it will certainly make your life easier if you need to later. If you have applications currently deployed that are OLE in-proc servers, instructions are provided in the Visual C++ 4.2 docs on how to retrofit doc object support.
More Good Stuff
Aside from the big guns trained on the Internet platform, Visual C++ 4.2 has been spruced up in a host of small but significant ways. In particular, Microsoft continues to refine the OLE Control Wizard. The generated code is faster and Visual C++ 4.2 controls will meet the OCX 96 guidelines, which include drawing optimizations and flicker-free activation. In addition, there is built-in support for windowless and nonrectangular objects.
You get a bit of beneficial technical backwash from Visual Basic¨ in the form of MFC support for both simple and complex data binding (at last!). This allows you to take advantage of one of the best games programmers have had going for years: data-bound controls. Data-bound controls link their grid display to the contents of an underlying relational database, dynamically adapting their appearance to its tables. Apex's DBGrid, a really fine little data grid control, is included in a selection of OLE controls that come with Visual C++ 4.2. If you aren't familiar with how data-bound controls can get a database application off the launch pad quickly, test drive DBGrid from within the resource editor. Another useful third-party tool in the Visual C++ 4.2 Professional Edition is the new Crystal Reports class library, which provides tools to export database reports in HTML format. Also, you finally get an up-to-the-minute version of the standard C++ library, including the features defined in the most recent C++ working paper.
After getting hammered in almost every conceivable way for the lack of efficiency of the heap manager, Microsoft has finally come up with a memory allocation scheme designed to please all of the people all of the time.
The new Small-Block Heap Manager responds with a transparent solution that helps all applications run faster and better when grabbing just a few bytes. The tricky part of beating the bad allocation performance rap has always been that certain strategies clearly work better for large allocations and other strategies work well for small allocations. The new heap-management scheme not only uses different algorithms for each kind of allocation, it also wisely lets developers get and set the threshold values that trigger the use of the big/little algorithms.
And Now for the Bad News
If your autobiography has a lot of chapters in which Win32s¨ plays a prominent role, you're going to have to write them out of the screenplay version. Visual C++ 4.1 closes the book on the cross-platform subset of Win32.
As sort of a consolation, Visual C++ 4.2 will ship packaged with a separate CD that includes Visual C++ 4.1 (Win32s support and all), but be forewarned that chaos is the likely result if you try to run both versions on the same workstation. Mostly this is a porting issue. Visual C++ 4.2 makes it clear that it's time to fold the tent and quietly steal out of 16-bit territory.
Visual C++ 4.2 contains a great deal of watershed technology. Not only does it considerably reduce the technical scale of programming on both the client and server end of network conversations, it delivers the tools for creating innovative distributed applications to a huge, literate population of MFC programmers who will be able to put the new tools to work almost immediately. The applications that are likely to grow out of the WinInet and ISAPI technologies reach far beyond the desktop.
From the September 1996 issue of Microsoft Systems Journal.