Developing 32-Bit SQL Server Applications for the Microsoft Windows NT Operating System

Christopher Moffatt
Microsoft Technical Resources Group

Abstract

The Microsoft® Windows NT™ operating system is an ideal platform for building robust, feature-rich applications for Microsoft SQL Server. The SQL Server Programmer’s Toolkit for Windows NT contains a 32-bit (Win32™ API) version of DB-Library™, making it possible to develop DB-Library client applications for Microsoft SQL Server on the Windows NT operating system.

The purpose of this technical article is to introduce DB-Library developers to the Microsoft Windows NT platform, address issues involved in porting existing DB-Library applications for the Windows™ version 3.x and OS/2® operating systems to the Windows NT operating system, and outline some of the ways in which DB-Library applications can take advantage of the Win32 application programming interface (API).

This technical article assumes that you are familiar with the DB-Library API, C programming, and the following documentation:

Building Win32-Based DB-Library Applications

The following sections describe uses relevant to building DB-Library™ applications using the Microsoft® Win32™ Application Programming Interface (API).

Win32 DB-Library Architecture

Internally, the Win32 version of the DB-Library dynamic-link library (DLL) is implemented similarly to the version for OS/2®. In Win32, as in OS/2, a separate operating system thread is spawned for each connection that DB-Library makes with SQL Server. Each instance of the DB-Library DLL that is loaded by a calling process gets a private data area, while sharing code.

The Win32 DB-Library architecture differs from the implementation in Microsoft Windows™ version 3.x. In Windows 3.x, the DB-Library DLL has a single data segment that is shared among all calling processes. W3DBLIB.DLL maintains DB-Library connections as a linked list of connections in a single data segment. This architecture is required because in Windows 3.x dynamic-link libraries have a single data segment that is shared among all calling processes. This necessitates the initialization and clean-up of the DB-Library DLL data structures through calls to the dbinit and dbwinexit functions.

Libraries

The DB-Library functions for Win32 are located in NTWDBLIB.DLL, and the named pipe driver is located in DBNMPNTW.DLL. Set the PATH environment variable to include the directory where the dynamic-link libraries reside.

Another file, NTWDBLIB.LIB, contains import definitions that your applications for the Win32 API use. Set the LIB environment variable to include the directory where NTWDBLIB.LIB resides.

Include Files

The SQL Server Programmer’s Toolkit for Windows NT™ contains the following include files:

Include file Contains
SQLFRONT.H Error codes and severity levels, miscellaneous definitions, and type definitions.
SQLDB.H Function prototypes for all DB-Library functions.

You can append the path to the INCLUDE environment variable to include the directory where the include files reside. Or you can use the /I compile line switch to point to the include file directory.

Because your applications for the Win32 API must always include the SQLDB.H file, you don’t need to define the DB-Library functions you use. These functions and their proper declarations are already defined in the include file. You must define an application’s operating system before including the DB-Library include files. Include the following statements at the beginning of all DB-Library applications that are designed for the Win32 API:

#define DBNTWIN32
#include <sqlfront.h>
#include <sqldb.h>

You can also define the operating system by using the /D compile line switch (for example, /DDBNTWIN32 instead of a #define declaration).

Compiling and Linking

The following example shows a simple build file that can be used with the NMAKE utility to compile and link debuggable DB-Library applications for the Win32 API:

NTLIB=\nt\sdk\lib\i386
DBLIB=\sql\dblib\lib
guilibs= $(NTLIB)\gdi32.lib $(NTLIB)\user32.lib $(NTLIB)\ 
     userrtl.lib $(NTLIB)\kernel32.lib $(NTLIB)\ntdll.lib \ 
     $(NTLIB)\libcmt.lib
all: sqltestw.exe
# Update the object file if necessary.
sqltestw.obj:  sqltestw.c sqltestw.h sqltestw.rc makefile
     cl386 -c -G3d -Di386=1 -DWIN32 sqltest.c
# Update the resource file if necessary.
sqltestw.res: sqltestw.rc sqltestw.h dilaogs.h dialogs.dlg makefile
rc -r -fo sqltestw.tmp sqltest.rc
cvtres -i386 sqltestw.tmp  -O sqltestw.res
del sqltestw.tmp
# Update the exe file if necessary.
sqltestw.exe: sql.obj sqltestw.obj sqltestw.res makefile
     link -machine:i386 -subsystem:windows -out:sqltestw.exe\
     -entry:WinMainCRTStartup\
     sqltestw.res sqltestw.obj $(guilibs)\
     ($DBLIB)\ntwdblib.lib

Porting Windows 3.x-Based DB-Library Applications to Win32

All DB-Library functions are completely portable between platforms. You should not have to modify any of your DB-Library calls. You simply need to define the application’s operating system at compile time (for example, #define DBNTWIN32 for Windows NT).

Because the Win32 API maintains compatibility with the Windows 3.x operating system, the Win32 DB-Library DLL supports all Windows 3.x-based DB-Library functions, even if they are not required. DB-Library functions present in Windows 3.x but not needed in Windows NT (for example, DBLOCKLIB and DBUNLOCKLIB) are accepted, but return without performing any actions.

Two DB-Library functions, dbprhead and dbprrow, not supported in Windows 3.x because they send output to STDIO, are supported in Win32 and can be used when developing applications for the CONSOLE subsystem. The dbprhead and dbprrow functions provide a convenient way to display the results of a query to the default output device.

If you have existing Windows-based DB-Library applications that you want to run with Windows NT, you can take three approaches:

Porting to Maintain DB-Library Compatibility for Windows 3.x

This section explains how to port a DB-Library application for Windows 3.x to the Windows NT operating system while retaining compatibility with applications for Windows 3.x.

API calls for Windows 3.x and C run-time

In general, you can change your source code to easily build 16-bit or 32-bit versions. For an in-depth discussion of writing applications for the Win32 API that retain compatibility with Windows 3.x, see the Microsoft Win32 API Programmer’s Reference, Volume 1.

The following guidelines apply to the development of applications for the Win32 API:

DB-Library functions and datatypes

All DB-Library functions are completely portable between operating systems. You simply need to define the target operating system at compile time.

Many DB-Library functions return values of type int. Note that the int datatype is a 16-bit value in Windows 3.x and a 32-bit value in the Win32 API. Therefore, verify that your application uses the correct type of variable to receive return values from DB-Library functions that return an int datatype. For example, a variable defined as the datatype short receives a correct int value from DB-Library in Windows 3.x but receives an incorrect value in the Win32 API.

Apart from verifying that variables receiving return values from DB-Library are of the correct type, no changes are needed to DB-Library functions.

Additional Porting Considerations

If you port an application for the Windows 3.x operating system to the Windows NT operating system and do not need to maintain compatibility with Windows 3.x, you can change your DB-Library code to remove redundant or unnecessary Windows 3.x functions.

Redundant Windows 3.x-specific DB-Library functions

The following Windows 3.x-specific DB-Library functions are redundant in the Win32 version of DB-Library:

dbinit

In the Win32 API, there is no need to call dbinit before issuing other DB-Library functions. As in the MS-DOS® and OS/2® operating systems, a Win32 call to dbinit returns a version string. In Windows 3.x, it is necessary to call dbinit to initialize DB-Library data structures.

Note   It is generally recommended that you retrieve the DB-Library version string, by calling dbinit, and make it viewable by the user (for example, in an “About” dialog box).

dbwinexit

This function is unnecessary in Win32.

DBLOCKLIB/DBUNLOCKLIB

In Win32, each process has a unique 32-bit linear virtual address space that allows it to address up to 4 gigabytes of memory (the 2 gigabytes in low memory are available to the user). In effect, all data is in one large segment, and you are freed from the concerns of segments, far pointers, and so on. Therefore, you don’t have to call the DBLOCKLIB and DBUNLOCKLIB macros before accessing pointers to data in the DB-Library DLL or pointers that pass data to the DB-Library DLL.

Changes to error/message handler registration

The following changes apply to the registration of error and message handlers for Win32:

For Windows 3.x:

// Define variables 
static FARPROC lpdbMsgHandler;
static FARPROC lpdbErrHandler;

// Get Procedure Instances
lpdbMsgHandler = MakeProcInstance((FARPROC)dbMsgHandler, hInst);
lpdbErrHandler = MakeProcInstance((FARPROC)dbErrHandler, hInst);

// Install the instances into dblib 
dbmsghandle(lpdbMessageHandler);
dberrhandle(lpdbErrorHandler);

For Win32:

// Install the instances into dblib 
dbmsghandle(dbMessageHandler);
dberrhandle(dbErrorHandler);

Preemptive multitasking (Win32) vs. cooperative multitasking (Windows 3.x)

To support asynchronous processing in the cooperative multitasking environment in Windows 3.x, each application must behave well and yield to the CPU at regular intervals. Therefore, when you process SQL Server queries in the Windows operating system, you need to use Windows-based timers or the PeekMessage function in combination with the DB-Library function calls for asynchronous processing (dbsqlsend, dbdataready, and dbsqlok). For more information about implementing asynchronous query processing in Windows 3.x, see the Microsoft SQL Server Programmer’s Reference for C (page 43).

The support for preemptive multitasking in the Win32 API simplifies the implementation of asynchronous query processing. The preemptive multitasking nature of the Windows NT operating system ensures that other processes always obtain CPU cycles regardless of any processing that another application is doing. Within a single process, asynchronous processing can be implemented using Win32 threads or by using the DB-Library functions that support asynchronous processing in conjunction with the PostMessage Windows function. For more information about asynchronous processing, see “Taking Advantage of Win32 API Features in DB-Library Applications,” later in this article.

Porting the SQLTEST3 Sample Application

This section uses the sample program SQLTEST3, provided with the DB-Library Programmer’s Toolkit, as an example of how to port a DB-Library application from Windows 3.x to Windows NT. Although it uses a simple application, this example illustrates how easily you can port applications for the Windows 3.x operating system to the Windows NT operating system.

Minimum port to Win32 (maintaining compatibility with Windows 3.x)

The following procedure shows how to modify the SQLTEST3 sample application to port it to the Win32 API while maintaining compatibility with Windows 3.x:

  1. Change the operating system definition:

    Change (Windows 3.x):

    #define DBMSWIN 
    

    To (Win32):

    #define DBNTWIN32
    
  2. Modify the function prototypes in SQLTEST3.H to be portable:

    Change (Windows 3.x):

    long FAR PASCAL SqlTestWndProc(HWND, unsigned, WORD, LONG);
    BOOL FAR PASCAL AboutSQL(HWND, unsigned, WORD, LONG);
    BOOL FAR PASCAL SelectSQL(HWND, unsigned, WORD, LONG);
    BOOL FAR PASCAL ConnectSQL(HWND, unsigned, WORD, LONG);
    int dbwinErrorHandler(DBPROCESS *, int, int, int, LPSTR, LPSTR);
    int dbwinMessageHandler(DBPROCESS *,DBINT,DBSMALLINT,DBSMALLINT,LPSTR);
    

    To (Win32):

    LONG APIENTRY SqlTestWndProc(HWND, UINT, DWORD, LONG);
    LONG APIENTRY  AboutSQL(HWND, UINT, DWORD, LONG);
    LONG APIENTRY SelectSQL(HWND, UINT, DWORD, LONG);
    LONG APIENTRY ConnectSQL(HWND, UINT, DWORD, LONG);
    int API dbwinErrorHandler(DBPROCESS *, int, int, int, LPSTR, LPSTR);
    int API dbwinMessageHandler(DBPROCESS* DBINTDBSMALLINT, DBSMALLINT, LPSTR);
    
  3. Modify the following function parameters and return values to match all changes made to the matching function prototypes in Step 2:
    SqlTestWndProc
    AboutSQL
    SelectSQL
    ConnectSQL
    dbwinErrorHandler
    dbwinMessageHandler
    

Full port to Win32

The following procedure shows how to modify the SQLTEST3 sample application to port it to the Win32 API without maintaining compatibility with Windows 3.x.

  1. Remove redundant DB-Library calls:
    DBLOCKLIB or DBUNLOCKLIB   (in three places)
    dbwinexit
    
  2. Change error/message handler registration:

    Change (Windows 3.x):

    static FARPROC lpdbwinMessageHandler; 
    static FARPROC lpdbwinErrorHandler;  
    ...
    lpdbwinMessageHandler = 
    MakeProcInstance((FARPROC)dbwinMessageHandler, hInst);
    lpdbwinErrorHandler = 
    MakeProcInstance((FARPROC)dbwinErrorHandler, hInst);
    dbmsghandle(lpdbwinMessageHandler);
    dberrhandle(lpdbwinErrorHandler);
    ...
    FreeProcInstance(lpdbwinMessageHandler)
    FreeProcInstance(lpdbwinErrorHandler);
    

    To (Win32):

    dbmsghandle(dbwinMessageHandler);
    dberrhandle(dbwinErrorHandler);
    
  3. Change dialog box instantiations:

    Modify all DialogBox function calls to pass the address of the function instead of calling MakeProcInstance and passing the pointer returned from this function.

  4. Delete the .DEF file (it is unnecessary):

Porting OS/2-Based DB-Library Applications to Win32

All DB-Library functions are completely portable across the OS/2 and Win32 APIs, and you should not need to modify to any of your DB-Library calls.

If you have existing OS/2-based DB-Library applications that you want to run with the Windows NT operating system, there are two basic approaches you can take:

OS/2 to Win32 API Conversion

As mentioned earlier, the topic of converting applications from OS/2 to the Win32 API is covered extensively in Converting OS/2 Applications to Windows. This section highlights two major areas of functionality that are found in a large number of OS/2-based DB-Library applications, namely thread management and synchronization mechanisms. (This information is taken directly from the Converting OS/2 Applications to Windows.)

Processes and threads

The Win32 API provides equivalent functionality to OS/2 process and thread calls. You can consider their functionality to be conceptually equivalent, except in the areas outlined below:

Semaphores and synchronization

There are two types of synchronization provided by both OS/2 and the Win32 API set: mutual exclusion and signaling.

Mutual exclusion

Mutual exclusion involves denying other threads of execution access to a critical section of code. Both OS/2 and Win32 have mechanisms designed to facilitate mutual exclusion within a process and mutual exclusion between processes.

Signaling

OS/2 semaphores can be used for signaling between processes. The OS/2 functions used for this purpose are DosSemSet, DosSemWait, DosSemSetWait, and DosMuxSemWait. The Win32 API provides the Event object, which implements a superset of this functionality. The Win32 functions used to manage the Event object include CreateEvent, OpenEvent, SetEvent, PulseEvent, and ResetEvent.

Taking Advantage of Win32 API Features in DB-Library Applications

The following sections describe how to take advantage of the unique features of the Win32 API.

Asynchronous Query Processing

Preemptive multitasking in the Win32 API makes implementing asynchronous query processing easy. There are several ways to implement asynchronous query processing, depending on whether you want your application to exhibit asynchronous behavior between processes or within a single process.

Memory Management in Win32

The 32-bit linear virtual memory address space available to processes in the Win32 API makes memory management simpler and cleaner. This applies to DB-Library programming as well as to any other programming for the Win32 API.

Although the memory management functions (GlobalAlloc, LocalAlloc, and so on) in Windows 3.x are supported by the Win32 API, consider using the C run-time functions malloc and free to perform dynamic memory management when you develop applications to run only with Win32. The malloc and free functions are not safe to use with Windows 3.x and OS/2 because of the segmented architecture of those systems. However, with Win32 these functions are both safe and elegant. In addition to the standard memory management functions (Global and Local, C run-time support), Win32 also provides heap and virtual memory management functions for applications that require specialized memory management.

Two specific areas of DB-Library programming can take advantage of the ability to address greater than 64K of data:

Threads in Win32

The multithreaded process structure of Win32 opens up powerful and elegant solutions to high-end applications. Each thread within a process has its own stack and machine state. On multiprocessor systems with the Windows NT platform, multiple threads can execute at the same time but on different processors. Applications that need to perform intensive processing, I/O, and so on, can do this in the background using threads and still offer a responsive user interface.

DB-Library and Reentrancy

The DB-Library API is basically non-reentrant, because all DB-Library functions that access a named pipe connection are not reentrant. You must serialize the DB-Library calls that access the named pipe connection in multithreaded applications.

Time-outs are also managed globally in DB-Library.

Structured Exception Handling

The Win32 API supports structured exception handling to aid the development of robust software and is made available to developers through compiler support. Consider using this functionality in your application. For more information about structured exception handling, see the Microsoft Win32 API Programmer’s Reference, Volume 1 (Chapter 11).

Symmetric Multiprocessing

The Windows NT platform supports symmetric multiprocessing (SMP), thereby allowing applications with multiple threads of execution to run across multiple processors. If your DB-Library application uses Win32 threads, it can advantage of Windows NT SMP support without any additional work being done.

Sample Code: SQLSTRES

SQLSTRES is a sample Windows NT-based DB-Library application. It can be used to simulate a large number of SQL Server clients to help determine the effect of “scaling up” an application when more users are added.

When a SQL Server client application is developed, questions often arise as to how response time and throughput will be affected when the application is deployed and a large number of users use the application simultaneously. Standard benchmarks are useful for getting a idea of performance and scalability, but these factors vary tremendously and are highly dependent on the types of queries and updates done by a specific application. In the development phase of an application, it is often difficult to simulate realistic operating environments with large numbers of workstations. The SQLSTRES application allows you to simulate a large number of users executing operations of your particular application from a single workstation:

The sample code described here can be found in the Microsoft Online Software Library or in the Microsoft Forum Software Library on CompuServe® in a file called SQLSTRES.ZIP. It can be located by searching on the keyword SQLSTRES, the Q number of this article in the Microsoft KnowledgeBase (Q87953), or S13580. SQLSTRES.ZIP was archived using the PKware file-compression utility.

SQLSTRES.ZIP contains the following files:

Filename Description
SQLSTRES.C Main source code file
SQLSTRES.H Source code header file
SQLSTRES.RC Windows-based resource script file
SQL.C Source code file for DB-Library–specific functions
SQL.H Header file for DB-Library–specific functions
DIALOGS.DLG Dialog box resource script file
DIALOGS.H Dialog box header file
DIALOGS.RES Windows NT–based Dialog Editor resource
MAKEFILE NMAKE File
TESTX.SQL Sample query file

References

Books

Microsoft SQL Server Programmer’s Reference for C (Part number 27445)

Microsoft Win32 API Programmer’s Reference, Volumes 1 & 2 (Microsoft Press)

Converting OS/2 Applications to Windows (Part number 098-35176)

Available Technical Notes

"Using Microsoft SQL Server on a Banyan VINES Network" (Part number 098-30193)

"Query Optimization Techniques: Contrasting Various Optimizer Implementations with Microsoft SQL Server" (Part number 098-30199)

"Microsoft Open Data Services: Application Sourcebook" (Part number 098-32078)

"Using Microsoft SQL Server on a Novell NetWare Network" (Part number 098-32655)

"Developing 32-Bit Open Data Services Applications for the Microsoft Windows NT Operating System" (Part number 098-36256)

Additional Information

To receive more information, contact Microsoft Inside Sales, Systems Software, at 1-800-227-4679.