Moving Unix Applications to Windows NT

Do you have suggestions on how to make this paper better? Please send them to brianmo@microsoft.com.

Preliminary draft:  March 6, 1994 (0.9)

Copyright 1993, 1994 Microsoft Corporation. All rights reserved. The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.
This document is for informational purposes only. Microsoft makes no warranties, express or implied, in this document.

Abstract

This paper is provided for the Unix® developer who is interested in learning about some of the aspects of moving Unix source code to the Microsoft® Windows NT™ operating system. Moving Unix applications to Windows NT is not only a process of potentially changing source code, but it's also one of learning about the tools, resources, information sources, and so forth. that have grown around the Windows™ and Windows NT phenomena.

"Unix" is a set of diverse, varied operating systems, with a rich cultural history. Many UNIphiles become so immersed in the Unix culture that they are not aware of other existing operating system cultures, nor do they tolerate "heretics" who dare suggest that there is innovation occurring outside of the Unix sphere. Similar cultures, though not as old, exist around Macintosh®, IBM® PCs, and perhaps strongest of all, Amiga. What is particularly interesting about discussions in which culture clashes do occur is that in general those people that are familiar with the operating system concepts in high-end (compared to PC) operating systems like Unix are the best positioned for exploiting the full power of Windows NT.

Putting religion aside for the duration of this discussion, this paper will focus on two main areas: a comparison of programmatic constructs and usages in the Unix environment (mainly centered around system services, deftly ignoring the specific issues of graphical programming), and how to best exploit Windows NT for those features, as well as the programming and information sources that developers for Windows and Windows NT find useful.

Overview of Windows NT

An overview of the features and functionality of Microsoft® Windows NT™ may be in order, both from the architectural as well as the lexical standpoint. Often in the comparison of Windows NT and Unix®, nearly identical concepts have dissimilar names!

Windows NT is a preemptive, multitasking, multi-user, portable (how many adjectives can be crammed in?) operating system designed around a modern micro-kernel architecture. The initial goals for Windows NT are:

Some of the operating system facilities in the first Windows NT product will be very familiar to a typical Unix developer, including:

Windows NT is able to host multiple programming interfaces, as well as support the semantics of those interfaces, through the use of subsystems layered on top of the Windows NT executive. In the first release, Windows NT provides a 32-bit Windows subsystem, a POSIX subsystem, and an OS/2 character mode subsystem. The subsystems are responsible for supplying both the APIs (system calls) as well as the state required for that particular environment.

Application vendors need only be concerned (for the most part) with the services and semantics of the subsystem for which they are programming. In Windows NT, due to the overwhelming acceptance of Windows by both the development community, the 40 million (as of November 1994) installed base, and the more than 1 million new users of Windows each month, the Win32 programming interface (Win32 is the name given to the set of 32-bit system calls that supports the semantics of Windows) is the most fully featured. This means that innovations and extensions will be seen in this programming interface first, and that programming tools will emphasize this interface. Of the 80,000 copies of the Windows NT software kit that have been purchased, nearly all of those are being used to develop applications for Windows using the Win32 interface.

(For an excellent treatment of the architecture of Windows NT in general, and the Windows NT executive in specific, please refer to the book Inside Windows NT by Helen Custer)

Windows 3.1 Programming

There are large numbers of very good texts, programming examples, magazine articles, and on-line information messages that provide an excellent introduction to Windows-based programming in general. Most information on Windows NT is written with the experience of the Windows developer in mind. It behooves any Unix developer interested in working with the Win32 API to take advantage of these resources.

Concepts that would likely be unfamiliar to a Unix developer include Windows message loops, interapplication communication using dynamic data exchange (DDE), the richness of the clipboard interface, object linking and embedding (OLE), Windows resources, and window function callbacks.

For these reasons, the books listed in Appendix B are highly recommended reading for someone unfamiliar with programming for Windows.

Character Applications on Windows NT

In addition to a graphical user interface, Windows NT supports a rich set of character-oriented APIs (see the glossary for a definition of API and other terms) that enable and encourage character-oriented applications. Sometimes it's just not necessary or cost-effective to provide a graphical user interface for an application (although the latest development tools are turning this into an incremental effort). In these cases, regular "stdio" calls can be used for terminal (Windows NT parlance is "console") I/O.

Due to the relative standardization among C run-time libraries (usually encompassing section 2 and section 3 system calls in the Unix programmer's manual), in many cases character applications that don't have dependencies on Unix-specific facilities (for example, termio) can be moved with a simple recompile—most "section 2" system calls have equivalents in the C run-time libraries provided with your C or C++ compiler for Windows NT. We'll discuss some of the changes that might be required to additional code under the general discussion of porting issues.

For developers who rely on "curses" functionality, there has recently been posted to CompuServe® (MSWIN32 Forum) and the Internet (ftp.microsoft.com) a subroutine library that provides this functionality, based on the Win32 console API functions.

Support for "dumb terminals" is something that has been demonstrated by third parties such as PanData (see the Resources section). The Microsoft view is that computers are going to continue to become more and more powerful, at lower and lower costs. It will be more effective from the cost and ease-of-use standpoints to use a computer running an application for Windows or Windows NT, communicating via RPC to an application server, than a similarly priced, less functional terminal.

Graphical Applications on Windows NT

Windows NT supports the GDI (Graphics Device Interface) and model for graphical programming. This portion of the Win32 API, which includes graphical object management as well as window management, was first popularized by the hordes of Windows 3.x developers. In the next release of Windows NT (currently code-named Daytona; due in the first half of 1994), OpenGL 3-D graphics are also added to the Windows NT APIs.

The Win32 GDI interface is different from Xlib, Xt, Motif, and any other programmatic interface for graphics (the bad news). The good news is that developers moving to Windows NT from Unix have noted that GDI provides a superset of X windows functionality, and that there are a wealth of tools for creating Microsoft Windows interfaces. For Unix source code that has been written with portability (to other UNIXs) in mind, such GUI tools can be used to develop all of the user interface (with little regard for learning the intricacies of Windows-based programming), and the non-graphical code can be plugged in. This code quilting process has been quite successful for a number of previously Unix-only developers. Many commercial Unix developers have come to view Windows NT as another port, albeit their highest-volume one.

For those who are looking for a stepping-stone approach to moving code quickly, sources to an "Xlib" (ntxlib.zip), which resolves to calls to the Win32 API, have been uploaded to CompuServe (MSWIN32 forum) and placed in the archives on ftp.uu.net (directory: vendor/microsoft/winnt-progs/xlib/ntxlib.zip). For in-house applications, this is often satisfactory; however, Windows users of commercial software using this method of porting will likely be disappointed in the user interface differences and lack of support for required Windows functionality such as the Clipboard and DDE.

Some software developers are using third-party tools (from vendors like XVT, Zinc, and others) to maintain a single set of source code for multiple Unix environments, as well as Windows NT. Most software vendors who expect to be successful with a commercial product for Windows NT are employing native Win32 calls for window and graphical operations; again, this is to take advantage of functionality unique to Windows and Windows NT operating systems, yet required by customer expectations—object linking and embedding (OLE), for example.

Of course, it's also possible to use an X server (to date, DEC®, Hummingbird, NCD, and others have demonstrated this functionality on Windows NT) to provide a graphical front end to an X client running on a network server—however, a typical user of Windows may be less than satisfied with differing semantics for manipulating the interface, and lack of interoperability (in most implementations) for such features as Clipboard, OLE, and so forth.

For those who must, there are X client libraries supported on Windows NT from companies such as Congruent, DEC, Hummingbird, and others. Again, these applications are NOT Windows NT applications, and require the use of an X server "terminal application" on the Windows NT desktop.

Many traditional Unix software vendors are moving their primary development to Windows NT, and "back porting" to Unix using Windows API layers such as those provided by Bristol Technologies, Mainsoft, or others. This enables them to exploit many of the Windows NT interapplication communication mechanisms, and add those features to Unix versions of their product if/when the underlying version of Unix gains the capability.

General Porting Issues

The Win32 API evolved from the Win16 API of Windows 3.x on MS-DOS®. The Win32 programming interface provides all of the functionality available for applications programming on Windows NT. In fact, the C (and other language) run-time libraries provided with compiler products for Windows NT eventually call the Win32 API. Consequently, there are many instances where the C run-time library call is subordinated by the function of a Win32 API—file I/O for example. The C run-time function fopen() eventually calls the Win32 CreateFile() interface.

For a developer, this means that "standard" C code will in general compile and run on Windows NT. However to utilize the full functionality provided by Windows NT (for example, for file mapping), or to implement functionality not provided for in the C run-time libraries, the specific Win32 calls should be used.

Always check the C run-time reference for your compiler first, to see if the system calls used in your application are supported. Your job may be easier than you think.

For most people moving code to Windows NT, the initial step is to get their application "barely" working, and then add Win32 enhancements. Changes will likely need to be made in makefiles, and some #include statements will probably have to be changed.

In the following sections we'll discuss the difference between the "Unix way" and the "Win32 way" of doing things as they might affect code being moved to Windows NT. Anecdotal information collected from a broad range of Unix software vendors who have moved their applications to Windows NT shows that those applications that are easiest to move are designed with "proper" coding methodologies in mind—separation of program flow into logical modules, isolation of dependencies on particular operating systems, flavors, or versions of operating systems, separation of graphical code and "business" code, and so on.

It may be most helpful to start out learning about the architecture of Windows NT from books such as Inside Windows NT, by Helen Custer. From this point forward, it is assumed that the reader has Unix programming experience, as well as the rudiments of Windows and Windows NT understanding.

The Windows NT Process Model

The Windows NT process model differs from that of Unix in a number of aspects, including process groups, terminal groups, setuid, memory layout, and so forth. For some programs, such as shells, a redesign of certain portions of the code is inevitable. Fortunately, most applications don't inherently rely on the specific semantics of Unix processes, since even this differs between Unix versions.

Quoting from the on-line help provided with the Windows NT SDK:

Win32 exposes processes and threads of execution within a process as objects. Functions exist to create, manipulate, and delete these objects.

A process object represents a virtual address space, a security profile, a set of threads that execute in the address space of the process, and a set of resources or objects visible to all threads executing in the process. A thread object is the agent that executes program code (and has its own stack and machine state). Each thread is associated with a process object which specifies the virtual address space mapping for the thread. Several thread objects can be associated with a single process object which enables the concurrent execution of multiple threads in a single address space (possible simultaneous execution in a multiprocessor system running Windows NT). On multiprocessor systems running Windows NT, multiple threads may execute at the same time but on different processors.

In order to support the process structure of Windows NT, APIs include:

Windows NT provides the ability to create new processes (CreateProcess()) and threads (CreateThread()). Rather than "inherit" everything always, as is done in Unix with the fork() call, CreateProcess() accepts explicit arguments that control aspects of process creation such as file handle inheritance, security attributes, debugging of the child process, environment, default directory, and so on. It is through the explicit creation of a thread or process with appropriate security descriptors that credentials are granted to the created entity.

Win32 does not provide the capability to "clone" a running process (and its associated in-memory contents). This is not such a hardship, since most Unix code fork()s and then immediately calls exec(). Applications that depend on the cloning semantics of fork() may have to be redesigned a bit to use threads (especially where large amounts of data sharing between parent and child occurs), or in some cases, to use IPC mechanisms to copy the relevant data between two distinct processes after the CreateProcess() call is executed.

If a child process is to inherit the handles of the creator process, the bInherit flag of the CreateProcess() call can be set. In this case, the child's handle table is filled in with handles valid in the context of the child process. If this flag is not specified, handles must be given away by using the DuplicateHandle()call.

Windows NT was not designed to support "dumb terminals" as a primary emphasis, so the concept of terminal process groups and associated semantics are not implemented. Applications making assumptions about groups of applications (for example, killing the parent process kills all child processes), will have to investigate the GenerateConsoleCtrlEvent() API, which provides a mechanism to signal groups of applications controlled by a parent process using the CREATE_NEW_PROCESS_GROUP flag in the CreateProcess() API. The on-line documentation (API32WH.HLP) provides the gory details under the Console API functions overview.

Programs making assumptions about the layout of processes in memory (for example, Gnu EMACS, which executes, then "dumps" the image of variables in memory to disk, which is subsequently "overlaid" on startup to reduce initialization time), especially the relationship of code segments to data and stack, will likely require modification. Generally, practices such as these are used to get around some operating system limitation or restriction. At this level, a rethinking of the structure of that part of the application is generally in order, to examine supported alternatives to the "hack" that was used (perhaps memory mapped files for particular cases like this). For those who must deal with an application's pages on this level, there is a mechanism by which a process may be opened (OpenProcess()), and individual memory pages, threads, and stacks examined or modified.

There is no direct equivalent of the Unix setuid(). There are, however, a number of Windows NT alternatives to use depending on the task to be accomplished. If the task at hand is a daemon that runs with a fixed user context, it would be best to use a Windows NT service (again, the on-line help is invaluable for this information). A Windows NT service is equivalent to a "daemon" running with fixed user credentials, with the added benefit of being locally or remotely administrated through standard Windows NT administration facilities. For instances when a process must "impersonate" a particular user, it's suggested that a server program be written that communicates through a named pipe to the client application. When a connection to the named pipe occurs, the ImpersonateNamedPipeClient() API can be used to assume the security context of the calling client (validated by the underlying Windows NT network authentication), to perform the required operation in the client's context.

In Windows NT, processes and threads are referred to by their handles as well as their unique numerical IDs. Windows NT is a multithreading operating system; consequently, the concept of a "unit of execution" is dissociated from the "context of execution". Multiple units of execution (threads) can exist simultaneously in the overall process context, and each thread also has its own stack and thread local storage. A rich set of functions is provided to manipulate both processes and threads. The CreateProcess() API provides the ability to start a process with the thread of execution suspended. CreateProcess() returns a structure containing a handle to both the created process and the suspended thread (as well as other information).

Unlike Unix, Windows NT only returns a status for child processes that have exited if a handle is open to that thread and process. There is no such thing as a zombie process in Windows NT—if all handles to that process and thread are closed, the process does not return any information, and no information is kept by the OS upon termination. If a child's exit code is required, the handle returned by CreateProcess() can be waited on for the exit code. The following example creates a process, waits for the return status, does nothing with it, and terminates:

#include <windows.h>
#include <stdio.h>
#define MAXNAMELEN    255
#define CMDLINE        "/c dir"
#define IMAGENAME      "\\nt\\bin\\cmd.exe"
#define WINTITLE       "CreateProcess DEMO"

main(){
BOOL    fSuccess,fExit;
DWORD    dwExitCode,dw;
STARTUPINFO    SI;
PROCESS_INFORMATION    pi;
HANDLE     hProcess,hThread;
SI.cb =sizeof(STARTUPINFO); SI.lpReserved = NULL;SI.lpDesktop=NULL;
SI.lpTitle="CreateProcess DEMO";SI.cbReserved2=0;SI.lpReserved2=NULL;
fSuccess = CreateProcess((LPTSTR)IMAGENAME, (LPTSTR)CMDLINE,
           (LPSECURITY_ATTRIBUTES)NULL, 
           (LPSECURITY_ATTRIBUTES)NULL,
           (BOOL)TRUE,(DWORD)0,NULL,NULL,
           (LPSTARTUPINFO)&SI,(LPPROCESS_INFORMATION)&pi); 
if (fSuccess) {  
    hProcess = pi.hProcess;    hThread = pi.hThread;
    printf("Process Id = %d\nThread Id = %d\n",pi.dwProcessId,pi.dwThreadId);
    dw = WaitForSingleObject(hProcess, INFINITE) ;
    if (dw != 0xFFFFFFFF) { /* if we saw success ... */
        /* pick up an exit code for the process */
        fExit = GetExitCodeProcess(hProcess, &dwExitCode) ;
    }
    /* close the process and thread object handles */
    CloseHandle(hThread) ;    CloseHandle(hProcess) ;
    printf("COMPLETED!\n");
}
else    printf("Failed to CreateProcess\n"); }

Various other interfaces are provided to examine and change aspects of both the process and thread. The OpenProcess() API could be used in combination with the debugging APIs ReadProcessMemory() and WriteProcessMemory() to examine/set memory segments (given appropriate security permission), and Get/SetThreadContext() could be used to manipulate the thread program characteristics (such as program counter and other information).

To kill the executing process, ExitProcess() can be used. For graphical applications this should be a method of "last resort," however, since most Windows-based applications will respond to a WM_QUERYENDSESSION Windows message and exit more gracefully. This is usually preferable, since the WM_QUERYENDSESSION message processing usually involves giving application users an opportunity to save their information.

Priority Classes

Windows NT provides a very flexible priority hierarchy, with four priority classes (of five levels each) to use when CreateProcess() or SetPriorityClass() APIs are used: IDLE, NORMAL, HIGH PRIORITY, and REALTIME. Within each class, plus or minus 2 priority levels are possible through the SetThreadPriority() API. With responsible use of priority classes and selective boosting of individual thread priorities, it's possible to perform soft real-time processing with Windows NT. In cases such as these, a +2 REALTIME priority thread will be waiting to be signaled, at which time it will accomplish its task and wait again. If a high-priority thread runs too long, it can affect the performance of the system (such as mouse movement, disk cache flushes, and so on). For this reason, the highest priority levels are restricted to processes executing in the Administrator security context.

For a discussion of priority classes and their effect on the system, please refer to the "Process and Threads Overview" section, "Scheduling priorities" topic in the on-line help in the Windows NT SDK.

Signals

The C run-time libraries provided with the Windows NT SDK provide support for the following signals: SIGFPE, SIGILL, SIGSEGV, SIGINT, SIGBREAK, SIGABRT, and SIGTERM. Additionally, SIGABRT and SIGTERM can be signaled by raise(). Signals generated or raise()d by a thread are always handled by that thread—signals can't be used for interthread or interprocess communication.

Typical use of signals in Unix applications occurs where nonblocking I/O occurs, and the application is signaled upon completion. Here's some typical Unix code for doing nonblocking file I/O:

{ ... }
set_fl(STDOUT_FILENO, O_NONBLOCK);   /* set stdout to do nonblocking file i/o */
bytes_written = write(STDOUT_FILENO, buffptr, numbytes);
{ ... maybe do some other stuff here ... }
/* turn off non-blocking file i/o */
clr_fl(STDOUT_FILENO,O_NONBLOCK);
{ ... }

In Windows NT, the preferred method of handling I/O completion or time-out is through an OVERLAPPED I/O structure, provided as a parameter to the ReadFile() or WriteFile() call. Usually an event object is created, which is used to indicate the status of the pending I/O. An example of event creation, usage in a file operation, and waiting on the completion of the I/O is provided in this code fragment (extracted from PDC.C in the samples provided with the Windows NT SDK). Note that this fragment actually consists of only eight calls; however, the formatting for expository purposes makes it verbose:

     IoCompletedEvent = CreateEvent( NULL,   // Not inherited
                                     TRUE,   // Manual reset
                                     FALSE,  // Initially reset
                                     NULL    // No name
                                   );
/* do other setup required */
 do_setup_routine();
/*  */
    //
    // Open the file using the fully qualified path. Specify the
    // sequential scan hint to the cache manager and if asynchronous
    // I/O will be used, specify the overlapped flag as well.
    //
    File = CreateFile( Path,
                       GENERIC_READ,
                       FILE_SHARE_READ,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_SEQUENTIAL_SCAN |
                         (ASyncIO ? FILE_FLAG_OVERLAPPED : 0),
                       NULL
                     );
    //
    // Since NULL might be a valid file object handle, failure
    // is indicated by a special return value.
    //
    if (File == INVALID_HANDLE_VALUE) {
        fprintf( stderr, "%s(0) : error %u: Unable to open file.\n",
                 Path,
                 GetLastError()
               );
        return;
        }
     //
     // We use an alertable wait in a loop, as I/O completion
     // will terminate the wait, even through the event we
     // are waiting on is not signalled.
     //
       //
       // Using asynchronous I/O, so queue the read operation. The file 
       // handle must remain open while the read operation is pending.
       //
       if (!ReadFileEx( File,
                    FileData,
                    FileSize,
                    &SearchRequest->OverlappedIO,
                   (LPOVERLAPPED_COMPLETION_ROUTINE) ProcessReadFileCompletion
                    )
          ) {
           fprintf( stderr, "%s(0) : error %u: Unable to queue read of file.\n,
                    Path,
                    GetLastError();
    /* other cleanup goes here */
      .... 
    }
/*    Now do something else, eventually dropping into :

*/
    /* Other stuff occurs here */ { }
    /* NOTE that the ReadFileEx is scheduled, and will */
    /* likely execute during this subsequent wait operation! */
     while (WaitForSingleObjectEx( IoCompletedEvent,
                                   0xFFFFFFFF,
                                   TRUE
                                  ) == WAIT_IO_COMPLETION
            ) {
         ;

This example shows how a completion routine can be used to process data that is read asynchronously. The source for the data could be a disk file, pipe, communications port, and so forth.

Note that the asynchronous I/O operation is scheduled as a deferred procedure call, and is not executed until the scheduling process Waits or Sleeps.

In other instances of signal use, Structured Exception Handling can generally be used instead of signals.

Some Unix applications use signals for interprocess communication (for example, SIGUSR2). With Windows NT, a signal is only raised on the thread that caused it, so signals cannot be used for IPC. Code that attempts to do this should use another IPC mechanism such as events or semaphores.

File I/O

A typical Unix application will use standard C run-time library calls for file I/O (that is, fopen(), fread(), fwrite(), fprint(), fclose()). For operations on files and most devices, these will work just fine. For character Windows NT applications, these calls can also be used for access to console input and output. Low level I/O (for example, creat(), read(), write()), although usable, should be avoided. Additionally, when using the Win32 API it is a bad idea to make assumptions regarding the file descriptor ordinals of stdin, stdout, and stderr.

To take advantage of 64-bit file access, asynchronous (overlapped) I/O, signaling of I/O completion, and so on, the Win32 APIs of CreateFile(), ReadFile(), and WriteFile() must be used. Note that the file handles used between these "levels" of I/O functionality are not interchangeable—don't try to use ReadFile to read information from an "fopen()-ed" file.

For concurrent access to a single file (either over the network, or on the same machine), to ensure exclusive access to certain records in a file, use the LockFile() API when using Windows NT file handles, and the locking() API when using the C run-time libraries.

Windows NT file systems may not be based on an "i-node" representation. The meaning of certain st structure members returned by the stat() call (for example, st.ino), although provided for compatibility, is meaningless. In general, it will always return zero. Code that uses the i-node number to test for file linking won't work as anticipated. You may wish instead to use the Win32 GetFileInformationByHandle() API, which returns a structure containing a 64-bit quantity guaranteed unique and constant for an open file on a given volume. Please refer to the API reference for more information on this call.

Since Windows NT can accommodate multiple file systems, with different file name characteristics, you may need to check whether the underlying file system can support the type of file names that your application expects. Windows NT ships with support for FAT file systems, HPFS file systems, NTFS file systems, network file systems, and CD-ROM file systems. See the discussion of File Systems for more information.

Binary vs. Text File I/O Using the C Run-Time Libraries

The C run-time libraries that are provided with the Windows NT SDK are derived from the Windows 3.1 C run-time libraries. As such, they differ from typical Unix C run times, since there is a distinction between text and binary file I/O. (Note that this is true for the fopen(), fclose(), and other functions, and NOT the Windows NT CreateFile(), ReadFile(), and WriteFile() functions.)

When a file is fopened using the "r" mode attribute, the C run-time libraries default to assume that text-mode file I/O is to be used. In text mode, CR-LF pairs denote end-of-line, and control-Z denotes end-of-file. A line read will silently ignore CR-LF, positioning the file pointer at the subsequent non-EOL character. On printf()s, all "\n"s will be translated to CR-LF.

To read with files in untranslated (binary) mode, the file mode "rb" should be used in the fopen() call. The analog for writing files is the "wb" file mode.

File Systems

A file system means many things to Windows NT. Network redirectors, file system drivers using magnetic media or optical media, object stores, and so on, all provide "file system" characteristics to Windows NT. Windows NT was designed to be able to work with these many different models, while providing a consistent access mechanism (CreateFile(), ReadFile(), WriteFile()). Windows NT also exposes the notion of "volumes," originating from "drives" as found in the more than one hundred million copies of MS-DOS in use.

Before an application generates a filename to be used on a given volume, GetVolumeInformation() should be used to determine the characteristics of the file system that influence the name—the maximum length of the filename, whether case is preserved, case is sensitive, or is Unicode-enabled. GetVolumeInformation() works on any file system that can be described by the root directory name, including network file systems, and special layered file systems.

In general, names for Windows NT should follow the following conventions (from the on-line Win32 API help file in the Windows NT software development kit):

Filename Conventions

Although each file system can have specific rules about the formation of individual components in a directory or filename, all file systems follow the same general conventions for combining components. For example, the FAT file system requires file and directory names to have the 8.3-character filename format; the HPFS file system allows names to be up to 254 characters; and the NTFS file system allows names to be up to 255 characters. All three file systems use the backslash (\) character to separate directory names and the filename when forming a path.

General rules for applications creating names for directories and files or processing names supplied by the user include the following:

By following the rules listed in this section, an application can create valid names for files and directories regardless of the file system in use.

Terminal Handling

As discussed previously, Windows NT does not provide dumb terminal support. It is a relatively simple process to move character applications to Windows NT using the Console functions provided by Win32 (see the on-line help or SDK manuals for more information), since functionality to support character deletion, insertion, cursor movement, and others, is provided.

A good mechanism for moving character applications to Windows NT would be to create a "virtual terminal" environment using Windows functions to paint text, scroll, position characters, and so on, in a graphical window. This solution would provide limited, although better, integration of Windows features such as Clipboard cut and paste, and would provide a solid foundation for addition of other Windows NT features that users have come to expect.

Security

The Windows NT security model is very different from that of Unix. Designed to be initially certifiable at the C2 level, and in subsequent releases eventually able to support B level security, security is pervasive throughout all aspects of the system. At the kernel level, security attributes, in the form of access control lists (ACLs), are kept on kernel objects. Threads and processes have Security IDs (SIDs) associated with them, which are validated against object ACLs.

This type of model does not translate directly to the User/Group/Permission model of Unix, and code that handles Unix security will likely need to be modified for Windows NT security.

The most common operations involve checking access to files, and changing the context of the executing process. The former functionality is provided by AccessCheck() APIs (for files, processes, threads, events, mutexes, semaphores, and so on), the latter provided by Impersonation.

A example of a service (provided in the appendix) illustrates the concept of impersonation. Service.c accepts a connection from a named pipe, assumes the user's context, gets some information, writes the information back to the named pipe, and reverts to the service's own security level.

For more information on security in Windows NT, please refer to the on-line Win32 API reference, or the documentation accompanying the Win32 SDK.

Memory Management

Windows NT provides a flat 32-bit address space, half of which is reserved for the OS, and half available to the process. This provides a separate 2 gigabytes of demand paged virtual memory per process. This memory is accessible to the software developer through the usual malloc() and free() memory allocation and deallocation routines, as well as some advanced Windows NT–specific mechanisms.

For a programmer desiring greater functionality for memory control, Windows NT also provides Virtual and Heap memory management APIs.

The advantage of using the virtual memory programming interface (VirtualAlloc(), VirtualLock(), VirtualQuery(), and others) is that the developer has much more control over whether backing store (memory committed in the paging [swap] file to handle physical memory overcommitment) is explicitly marked, and removed from the available pool of free blocks. With malloc(), every call is assumed to require the memory to be available upon return from the function call to be used. With VirtualAlloc() and related functions, the memory is reserved, but not committed, until the page on which an access occurs is touched. By allowing the application to control the commitment policy through access, fewer system resources are used. The tradeoff is that the application must also be able to handle the condition (presumably with structured exception handling) of an actual memory access forcing commitment; examples are provided in the SDK to illustrate this situation.

Heap APIs are provided to make life easier for applications that make use of memory using stack discipline. Multiple heaps can be initialized, each growing/shrinking with subsequent accesses. Synchronization of access to allocated heaps can be done either explicitly through Windows NT synchronization objects, or by using an appropriate parameter at the creation of a heap, all access to memory in that particular heap is synchronized between threads in the process.

Memory mapped files are also provided in Windows NT. This provides a convenient way to access disk data as memory, with the Windows NT kernel managing paging. This memory may be shared between processes by using CreateFileMapping() followed by MapViewOfFile().

Windows NT provides thread local storage (TLS) to accommodate the needs of multithreaded applications. Each thread of a subprocess has its own stack, and may have its own memory to store various information. For more information on TLS, please refer to the Windows NT Programming Reference.

Threading

Windows NT is the first operating system to provide a consistent multithreading API across multiple platforms. A thread is a unit of execution in a process context that shares global memory state with other threads in that context (if any). When a process is created in Windows NT, memory is allocated for it, state is set up in the system, and a thread object is created. To start an additional thread in a currently executing process, the CreateThread()call is used with a pointer to executable code passed as one of the parameters.

Windows NT supports a number of different types of multiprocessing hardware. On these designs, it's possible for different processors to be running different threads of an application simultaneously. Care must be taken in using threads in an application to synchronize access to common resources between threads. Fortunately, Windows NT has very rich synchronization facilities.

Most Unix developers don't use threads in their applications, since support is not consistent between Unix platforms. For more information on thread usage, please refer to the Windows NT SDK documentation.

Handles

Handles don't have a direct mapping from Unix, however they're very important to Win32-based applications, and deserve discussion. When kernel objects (that is, threads, processes, files, semaphores, mutexes, events, pipes, mailslots, and communications devices) are created or opened using the Win32 API, a HANDLE is returned. This handle is a 32-bit quantity that is an index into a handle table specific to that process. Handles have associated ACLs, or Access Control Lists, that Windows NT uses to check against the security credentials of the process. Handles can be obtained by explicitly creating them (usually when an object is created), as the result of an open operation (for example, OpenEvent()) on a named object in the system, inherited as the result of a CreateProcess() operation (a child process inherits an open handle from its parent process if inheritance was specified when the original handle was created and if the child process was created with the "inherit handles" flag set), or "given away" by DuplicateHandle(). It is important to note that unless one of these mechanisms is used, a handle will be meaningless in the context of a process.

For example, suppose process 1 calls CreateEvent() to return a handle that happens to have the ordinal value 0x1FFE. This event will be used to coordinate an operation between different processes. Process 2 must somehow get a handle to the event that process 1 created. If process 1 somehow "conjures" that the right value to use is 0x1FFE, it still will not have access to the event created by process 1, since that handle value means nothing in the context of process 2. If, instead, process 1 calls DuplicateHandle()with the handle of process 2 (acquired through calling OpenProcess() with the integral ID of process 2), a handle that can be used by process 2 is created. This handle value can be communicated back to process 1 through some IPC mechanism.

Handles that are used for synchronization (semaphores, mutexes, events) as well as those that may be involved in asynchronous I/O (named pipes, files, communications) may be used with WaitForObject() and WaitForMultipleObject(), which are functionally similar to the select() call in Unix.

Networking—IPC

For those familiar with Unix networking, which is primarily TCP/IP-based, Windows NT provides a "Berkeley-flavored" socket interface through the Windows Sockets API. Windows Socket handles can be used as Windows NT file handles can be in the ReadFile() and WriteFile() functions, as well as inherited by child threads/processes (see preceding paragraphs). Windows NT socket handles are also waitable. All port numbers are available for use by application programs—there are no restrictions on range. The details of the Windows sockets interface are detailed in the specification available from ftp.microdyne.com, or vax.ftp.com. The Windows sockets API is also included in the form of an on-line help file (see subsequent section on on-line help) in the Windows NT SDK.

The file sharing protocol built into Windows NT is based on SMB (Server Message Block). NFS file systems are supplied by third parties, such as Sun, NetManage, and FTP Software. A public domain NFS server (SOSSNTR*.ZIP) exists on many well-known Internet sites (try ftp.cica.indiana.edu).

Other types of IPC include named pipes (LAN Manager and MS-DOS–based applications use these), NetBIOS, Windows sockets for IPX/SPX (Novell networking), and mailslots. Windows NT provides connection-oriented DCE RPC, which can be used with other DCE-compatible RPC clients and servers.

Development Roadmap

The Windows NT Software Development Kit (SDK) includes all of the libraries, include files, sample source code, help files, and tools to develop 32-bit applications for Windows NT and Windows 3.1.

The Windows NT Device Driver Kit (DDK) contains everything necessary to write device drivers for Windows NT, including sample source code for most of the drivers in the prerelease. The SDK is a prerequisite for the DDK.

Languages available for Windows NT from Microsoft include C, C++, and FORTRAN; other companies provide these languages and more.

Most Unix developers are accustomed to a rich set of "Unix tools" (for example, awk, sed, lex, grep, and so on). Fortunately, many of the initial developers moved tools to Windows NT, and in many cases made these tools available in source form on various well-known Internet sites. Commercial vendors (for example, Congruent, MKS, Hamilton) have filled the void with tools, shells, and libraries that are available now. Please see the appendix on resources.

WinHelp as a Resource

The Windows NT SDK provides a number of useful resources for the developer, one of which is on-line help (WinHelp). To access this facility, start the WinHelp application through one of the "question mark" icons, or launch "winhlp32" from the Program Manager. WinHelp files (generally located in the \mstools\bin directory) exist in the SDK for topics such as the Win32 programming interface (API32WH.HLP), Microsoft C run-time library (MSC.HLP), as well as for some of the other programming tools such as the Dialog Editor (dlgedit.hlp), and Resource Compiler (rc.hlp). Since many of the help files include sample source code and helpful hints, you should consult these frequently. Winhelp provides the ability to search by keywords, do hypertext jumping to related topics, print the viewed information, or even paste text and code samples to the Clipboard.

Figure 1. Typical Windows help screen

Appendix A. Service Sample Source Code

This appendix contains code from the Windows NT Software Development kit for a simple service. In Windows NT, a service is the equivalent of a Unix Daemon. This code illustrates:

The Makefile

When moving code from Unix to Windows NT, you'll need to modify your makefiles to include the following line:

!include <ntwin32.mak>

This line sets up the processor variables, library files, and so on. It cannot be stressed enough that you should use the sample makefiles as templates for your code moving from Unix. Here is the makefile in its entirety:

.
# Nmake macros for building Windows 32-Bit apps
!include <ntwin32.mak>
all: simple.exe instsrv.exe client.exe
# Update the object file if necessary
.c.obj:
    $(cc) $(cdebug) $(cflags) $(cvarsmt) $*.c
simple.exe: simple.obj
    $(link) $(linkdebug) $(conflags) -out:simple.exe simple.obj
        $(conlibsmt) advapi32.lib
instsrv.exe: instsrv.obj
    $(link) $(linkdebug) $(conflags) -out:instsrv.exe instsrv.obj
        $(conlibsmt) advapi32.lib
client.exe: client.obj
    $(link) $(linkdebug) $(conflags) -out:client.exe client.obj
        $(conlibsmt)

Note that the $(conflags) variables take care of most of the declarations you'll need to make for a console application.

Service Sample Source Code—README.TXT

The following is the README.TXT file from the Simple Service example. Please note that to install the service, you must be logged on as Administrator on your Windows NT machine.

Sample: Simple Service

Summary:

The Simple Service sample demonstrates how to create and install a service.

In this particular case, the service merely opens a named pipe of the name \\.\pipe\simple, and waits for traffic. If it receives anything, it surrounds the input with:

Hello! [<input goes here>]

and sends it back down the pipe to the client.

The service can be Started, Stopped, Paused, and Continued.

To install the service, first compile everything, and then user INSTSRV to install SimpleService as follows:

 instsrv SimpleService <location of simple.exe>

Now all you have to do is start it, either using the "net start" method or via the control panel Services applet.

Once the service has been started, you can use the CLIENT program to verify that it really is working, using the syntax:

    client \\.\pipe\simple Hello

which should return the response:

    Hello! [Hello]

If, after playing with the sample you wish to remove the service, simple say:

    instsrv SimpleService remove

Note that INSTSRV can be a little dangerous—it'll install and remove any service you tell it to, so be careful.

Additional reference words:

CloseServiceHandle, InitializeSecurityDescriptor, SetSecurityDescriptorDacl, SetServiceStatus, OpenSCManager, StartServiceCtrlDispatcher, RegisterEventSource, DeregisterEventSource, RegisterServiceCtrlHandler

Service Sample Source Code—SERVICE.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples.
*       Copyright (C) 1993 Microsoft Corporation.
*       All rights reserved.
*       This source code is only intended as a supplement to
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the
*       Microsoft samples programs.
\******************************************************************************/
///////////////////////////////////////////////////////
//
//  Service.c --
//      main program for Service sample.
//
//      This service simply opens a named pipe
//      (called \\.\pipe\simple), and reads from it.
//      It then mangles the data passed in and writes
//      the result back out to the pipe.
//
//      The simple service will respond to the basic
//      service controller functions, i.e. Start, Stop, and Pause.
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
// this event is signaled when the worker thread ends
HANDLE                  hServDoneEvent = NULL;
SERVICE_STATUS          ssStatus;       // current status of the service
SERVICE_STATUS_HANDLE   sshStatusHandle;
DWORD                   dwGlobalErr;
DWORD                   TID = 0;
HANDLE                  threadHandle = NULL;
HANDLE                  pipeHandle;

//  declare the service threads:
//
VOID    service_main(DWORD dwArgc, LPTSTR *lpszArgv);
VOID    service_ctrl(DWORD dwCtrlCode);
BOOL    ReportStatusToSCMgr(DWORD dwCurrentState,
                            DWORD dwWin32ExitCode,
                            DWORD dwCheckPoint,
                            DWORD dwWaitHint);
VOID    StopSampleService(LPTSTR lpszMsg);
VOID    die(char *reason);
VOID    worker_thread(VOID *notUsed);
VOID    StopSimpleService(LPTSTR lpszMsg);

//  main() --
//      All main does is call StartServiceCtrlDispatcher
//      to register the main service thread. When the
//      API returns, the service has stopped, so exit.
//
VOID
main()
{
    SERVICE_TABLE_ENTRY dispatchTable[] = {
        { TEXT("SimpleService"), (LPSERVICE_MAIN_FUNCTION)service_main },
        { NULL, NULL }
    };
    if (!StartServiceCtrlDispatcher(dispatchTable)) {
        StopSimpleService("StartServiceCtrlDispatcher failed.");
    }
}

//  service_main() --
//      This function takes care of actually starting the service,
//      informing the service controller at each step along the way.
//      After launching the worker thread, it waits on the event
//      that the worker thread will signal at its termination.
//
VOID
service_main(DWORD dwArgc, LPTSTR *lpszArgv)
{
    DWORD                   dwWait;
    PSECURITY_DESCRIPTOR    pSD;
    SECURITY_ATTRIBUTES     sa;
    // register our service control handler:
    //
    sshStatusHandle = RegisterServiceCtrlHandler(
                                    TEXT("SimpleService"),
                                    service_ctrl);
    if (!sshStatusHandle)
        goto cleanup;
    // SERVICE_STATUS members that don't change in example
    //
    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ssStatus.dwServiceSpecificExitCode = 0;

    // Report the status to Service Control Manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        1,                     // checkpoint
        3000))                 // wait hint
        goto cleanup;
    // Create the event object. The control handler function signals
    // this event when it receives the "stop" control code.
    //
    hServDoneEvent = CreateEvent(
        NULL,    // no security attributes
        TRUE,    // manual reset event
        FALSE,   // not-signalled
        NULL);   // no name
    if (hServDoneEvent == (HANDLE)NULL)
        goto cleanup;
    // Report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        2,                     // checkpoint
        3000))                 // wait hint
        goto cleanup;
    // Create a security descriptor that allows anyone to write to the pipe...
    //
    pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
                SECURITY_DESCRIPTOR_MIN_LENGTH);
    if (pSD == NULL) {
        StopSimpleService("LocalAlloc pSD failed");
        return;
    }
    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
        StopSimpleService("InitializeSecurityDescriptor failed");
        LocalFree((HLOCAL)pSD);
        return;
    }
    // Add a NULL disc. ACL to the security descriptor.
    //
    if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)) {
        StopSimpleService("SetSecurityDescriptorDacl failed");
        LocalFree((HLOCAL)pSD);
        return;
    }
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = pSD;
    sa.bInheritHandle = TRUE;       // why not...
    // Open our named pipe...
    //
    pipeHandle = CreateNamedPipe(
                    "\\\\.\\pipe\\simple",  // name of pipe
                    PIPE_ACCESS_DUPLEX,     // pipe open mode
                    PIPE_TYPE_MESSAGE |
                    PIPE_READMODE_MESSAGE |
                    PIPE_WAIT,              // pipe IO type
                    1,                      // number of instances
                    0,            // size of outbuf (0 == allocate as necessary)
                    0,            // size of inbuf
                    1000,         // default time-out value
                    &sa);         // security attributes
    if (!pipeHandle) {
        StopSimpleService("CreateNamedPipe");
        LocalFree((HLOCAL)pSD);
        return;
    }
    // Start the thread that performs the work of the service.
    //
    threadHandle = (HANDLE)_beginthread(
                    worker_thread,
                    4096,       // stack size
                    NULL);      // argument to thread
    if (!threadHandle)
        goto cleanup;
    // Report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_RUNNING, // service state
        NO_ERROR,        // exit code
        0,               // checkpoint
        0))              // wait hint
        goto cleanup;
    // Wait indefinitely until hServDoneEvent is signaled.
    //
    dwWait = WaitForSingleObject(
        hServDoneEvent,  // event object
        INFINITE);       // wait indefinitely
cleanup:
    if (hServDoneEvent != NULL)
        CloseHandle(hServDoneEvent);

    // Try to report the stopped status to the service control manager.
    //
    if (sshStatusHandle != 0)
        (VOID)ReportStatusToSCMgr(
                            SERVICE_STOPPED,
                            dwGlobalErr,
                            0,
                            0);
    // When SERVICE MAIN FUNCTION returns in a single service
    // process, the StartServiceCtrlDispatcher function in
    // the main thread returns, terminating the process.
    //
    return;
}

//  service_ctrl() --
//      This function is called by the Service Controller whenever
//      someone calls ControlService in reference to our service.
//
VOID
service_ctrl(DWORD dwCtrlCode)
{
    DWORD  dwState = SERVICE_RUNNING;
    // Handle the requested control code.
    //
    switch(dwCtrlCode) {
        // Pause the service if it is running.
        //
        case SERVICE_CONTROL_PAUSE:
            if (ssStatus.dwCurrentState == SERVICE_RUNNING) {
                SuspendThread(threadHandle);
                dwState = SERVICE_PAUSED;
            }
            break;
        // Resume the paused service.
        //
        case SERVICE_CONTROL_CONTINUE:
            if (ssStatus.dwCurrentState == SERVICE_PAUSED) {
                ResumeThread(threadHandle);
                dwState = SERVICE_RUNNING;
            }
            break;
        // Stop the service.
        //
        case SERVICE_CONTROL_STOP:
            dwState = SERVICE_STOP_PENDING;
            // Report the status, specifying the checkpoint and waithint,
            //  before setting the termination event.
            //
            ReportStatusToSCMgr(
                    SERVICE_STOP_PENDING, // current state
                    NO_ERROR,             // exit code
                    1,                    // checkpoint
                    3000);                // waithint
            SetEvent(hServDoneEvent);
            return;
        // Update the service status.
        //
        case SERVICE_CONTROL_INTERROGATE:
            break;
        // invalid control code
        //
        default:
            break;
    }
    // Send a status response.
    //
    ReportStatusToSCMgr(dwState, NO_ERROR, 0, 0);
}

//  worker_thread() --
//      This function does the actual nuts and bolts work that
//      the service requires. It will also Pause or Stop when
//      asked by the service_ctrl function.
//
VOID
worker_thread(VOID *notUsed)
{
    char                    inbuf[80];
    char                    outbuf[80];
    BOOL                    ret;
    DWORD                   bytesRead;
    DWORD                   bytesWritten;
    // Okay, our pipe has been creating, let's enter the simple
    //  processing loop...
    //
    while (1) {
        // Wait for a connection...
        //
        ConnectNamedPipe(pipeHandle, NULL);
        // Grab whatever's coming through the pipe...
        //
        ret = ReadFile(
                    pipeHandle,     // file to read from
                    inbuf,          // address of input buffer
                    sizeof(inbuf),  // number of bytes to read
                    &bytesRead,     // number of bytes read
                    NULL);          // overlapped stuff, not needed
        if (!ret)
            // Pipe's broken... go back and reconnect
            //
            continue;
        // Assume the security credentials of the connected client
        ImpersonateNamedPipeClient(pipeHandle);
        // Now we're using the security credentials of the attached client. 
        // this is a good place to do some processing, but we're not going to
        // do much at all:

        // munge the string
        //
        sprintf(outbuf, "Hello! [%s]", inbuf);

       // Now we'll revert back to the security context of the service;
    
       RevertToSelf();
        // send it back out...
        //
        ret = WriteFile(
                    pipeHandle,     // file to write to
                    outbuf,         // address of output buffer
                    sizeof(outbuf), // number of bytes to write
                    &bytesWritten,  // number of bytes written
                    NULL);          // overlapped stuff, not needed
        if (!ret)
            // Pipe's broken... go back and reconnect
            //
            continue;
        // Drop the connection...
        //
        DisconnectNamedPipe(pipeHandle);
    }
}

// utility functions...

// ReportStatusToSCMgr() --
//      This function is called by the ServMainFunc() and
//      ServCtrlHandler() functions to update the service's status
//      to the service control manager.
//
BOOL
ReportStatusToSCMgr(DWORD dwCurrentState,
                    DWORD dwWin32ExitCode,
                    DWORD dwCheckPoint,
                    DWORD dwWaitHint)
{
    BOOL fResult;
    // Disable control requests until the service is started.
    //
    if (dwCurrentState == SERVICE_START_PENDING)
        ssStatus.dwControlsAccepted = 0;
    else
        ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
            SERVICE_ACCEPT_PAUSE_CONTINUE;
    // These SERVICE_STATUS members are set from parameters.
    //
    ssStatus.dwCurrentState = dwCurrentState;
    ssStatus.dwWin32ExitCode = dwWin32ExitCode;
    ssStatus.dwCheckPoint = dwCheckPoint;
    ssStatus.dwWaitHint = dwWaitHint;
    // Report the status of the service to the service control manager.
    //
    if (!(fResult = SetServiceStatus(
                sshStatusHandle,    // service reference handle
                &ssStatus))) {      // SERVICE_STATUS structure
        // If an error occurs, stop the service.
        //
        StopSimpleService("SetServiceStatus");
    }
    return fResult;
}

// The StopSimpleService function can be used by any thread to report an
//  error, or stop the service.
//
VOID
StopSimpleService(LPTSTR lpszMsg)
{
    CHAR    chMsg[256];
    HANDLE  hEventSource;
    LPTSTR  lpszStrings[2];
    dwGlobalErr = GetLastError();
    // Use event logging to log the error.
    //
    hEventSource = RegisterEventSource(NULL, TEXT("SimpleService"));
    sprintf(chMsg, "SimpleService error: %d", dwGlobalErr);
    lpszStrings[0] = chMsg;
    lpszStrings[1] = lpszMsg;
    if (hEventSource != NULL) {
        ReportEvent(hEventSource, // handle of event source
            EVENTLOG_ERROR_TYPE,  // event type
            0,                    // event category
            0,                    // event ID
            NULL,                 // current user's SID
            2,                    // strings in lpszStrings
            0,                    // no bytes of raw data
            lpszStrings,          // array of error strings
            NULL);                // no raw data
        (VOID) DeregisterEventSource(hEventSource);
    }
    // Set a termination event to stop SERVICE MAIN FUNCTION.
    //
    SetEvent(hServDoneEvent);
}

Service Sample Source Code—INSTSRV.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/
///////////////////////////////////////////////////////
//
//  InstSrv.c --
//
//      This program demonstrates the use of the OpenSCManager and
//      CreateService APIs to install the Simple service sample.
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
SC_HANDLE   schService;
SC_HANDLE   schSCManager;
VOID
InstallService(LPCTSTR serviceName, LPCTSTR serviceExe)
{
    LPCTSTR lpszBinaryPathName = serviceExe;
    schService = CreateService(
        schSCManager,               // SCManager database
        serviceName,                // name of service
        serviceName,       // name to display (new parameter after October beta)
        SERVICE_ALL_ACCESS,         // desired access
        SERVICE_WIN32_OWN_PROCESS,  // service type
        SERVICE_DEMAND_START,       // start type
        SERVICE_ERROR_NORMAL,       // error control type
        lpszBinaryPathName,         // service's binary
        NULL,                       // no load ordering group
        NULL,                       // no tag identifier
        NULL,                       // no dependencies
        NULL,                       // LocalSystem account
        NULL);                      // no password
    if (schService == NULL) {
        printf("failure: CreateService (0x%02x)\n", GetLastError());
        return;
    } else
        printf("CreateService SUCCESS\n");
    CloseServiceHandle(schService);
}
VOID
RemoveService(LPCTSTR serviceName)
{
    BOOL    ret;
    schService = OpenService(schSCManager, serviceName, SERVICE_ALL_ACCESS);
    if (schService == NULL) {
        printf("failure: OpenService (0x%02x)\n", GetLastError());
        return;
    }
    ret = DeleteService(schService);
    if (ret)
        printf("DeleteService SUCCESS\n");
    else
        printf("failure: DeleteService (0x%02x)\n", GetLastError());
}
VOID
main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("usage: instsrv <service name> <exe location>\n");
        printf("           to install a service, or:\n");
        printf("       instsrv <service name> remove\n");
        printf("           to remove a service\n");
        exit(1);
    }
    schSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
    if (!stricmp(argv[2], "remove"))
        RemoveService(argv[1]);
    else
        InstallService(argv[1], argv[2]);
    CloseServiceHandle(schSCManager);
}

Service Sample Source Code—CLIENT.C

/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/
////////////////////////////////////////////////////////
//
//  Client.c --
//
//      This program is a command line-oriented demonstration 
//      of the Simple service sample.
//
//      Copyright 1993, Microsoft Corp. All Rights Reserved
//
//  History:
//
//      who         when            what
//      ---         ----            ----
//      davidbro    2/3/93          creation
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
VOID
main(int argc, char *argv[])
{
    char    inbuf[80];
    char    outbuf[80];
    DWORD   bytesRead;
    BOOL    ret;
    if (argc != 3) {
        printf("usage: client <pipename> <string>\n");
        exit(1);
    }
    strcpy(outbuf, argv[2]);
    ret = CallNamedPipe(argv[1], outbuf, sizeof(outbuf),
                                 inbuf, sizeof(inbuf),
                                 &bytesRead, NMPWAIT_WAIT_FOREVER);
    if (!ret) {
        printf("client: CallNamedPipe failed, GetLastError = %d\n",
                GetLastError());
        exit(1);
    }
    printf("client: received: %s\n", inbuf);
}

Appendix B. Recommended Reading for First-Time Programmers

Programming Windows 3.1, by Charles Petzold; Microsoft Press

Inside Windows NT, by Helen Custer; Microsoft Press

Appendix C. More Resources

Newsgroups:

comp.os.ms-windows.programmer.win32
comp.os.ms-windows.nt.setup
comp.os.ms-windows.nt.misc
comp.os.ms-windows.announce

This paper, as well as the following appendix ("UNIX Support for Windows NT") is available from ftp.microsoft.com in the DRG/Unix-to-Windows directory. The NFS_XWIN.DOC file, which is reproduced in Appendix D, is a good place to look for the most up-to-date information. Please send updates or information on products that you find useful to brianmo@microsoft.com.

Appendix D (NFS_XWIN.DOC from ftp.microsoft.com). UNIX Support for Windows NT

Draft: November 2, 1993

NFS Clients and Servers

Beame & Whiteside
Products: NFS client and server
Main: 416-765-0822

FTP Software, Inc.
Products: NFS client and server
Main:  800-282-4387; 508-685-4000
Direct Sales: 508-685-3300
Fax: 508-794-4477

NetManage
Product: Chameleon NFS32
·NFS client and server
Main: 408.973.7171
Fax: 408.257.6405

SunSelect
Product: PC NFS for Windows NT
·NFS client, no announced server support
Main:  800-24SELECT; 508-442-0000
Fax:  508-250-5070

X Windows Servers and Clients

Congruent Corporation
Product: NtNiX - X Client Support for Windows NT (Intel or MIPS)
·all standard X libraries, X-terminal support, X clients
·development tools: imake, GNU make, sed, cpp, gawk, ...
·Motif support
·multi-user NT facility
Main:  212-431-5100
Fax:  212-219-1532
E-Mail: info@congruent.com

Digital Equipment Corp (DEC)
Products: eXcursion for Windows NT (X-Windows Server)
·call for other products and support for Windows NT
Main: 1-800-DEC-INFO xNT
Fax: 508-486-2311

AGE Logic, Inc.
Products: Xoftware for Windows
·provides full 32-bit X11R5 server support
Main:  619-455-8600
Fax: 619-597-6030

Network Computing Devices, Inc. (PC-X Division)
Products: PC-Xware for Windows NT (X-Windows Server)
Main: 503-641-2200
Direct Sales: 800-793-7638
Fax: 503-643-8642

Hummingbird Communications, Ltd.
Products: HCL-eXceed for Windows NT
Main:  416-470-1203
Fax: 416-470-1207

JSB Corp.
Product: Multiview/X
Main:  800-359-3408; 408-438-8300
Fax: 408-438-8360

Unix Tools and Utilities

Windows NT Product
Windows NT includes several UNIX utilities including TELNET, FTP, FTPD, and RSH.

Congruent Corporation
Products: Toolbuster - CD-ROM loaded with GNU Utilities for Windows NT
·GNU toolset, compilers. libraries, debuggers, editors, and utilities
·on-line documentation
·executables for Intel, Alpha, and MIPS
·RCS component
Main:  212-431-5100
Fax:  212-219-1532
E-Mail: info@congruent.com

Mortice Kern Systems (MKS), Inc.
Products: MKS Toolkit for Windows NT
Main:  519-884-2251
Direct Sales: 800-265-2797
Fax: 519-884-8861

Hippo Software
Products: HIPPIX
·provides a library that supports over 90% of the functions of the POSIX 1003.1 API working with the Win32 subsystem
·provides a suite of over 100 UNIX utilities including: SH, VI, AWK, GREP, MAKE, LEX, YACC, RCS
E-Mail: CompuServe: 72360,2675; Internet: hippix-info@hippo.com

Hamilton Laboratories
Products: C Shell for Windows NT
Main:  508-358-5715
Fax: 508-358-1113

Software Innovations
·All products available for Intel, MIPS, and Alpha.
Product: The Connectivity Kit for Windows NT
·TELNETD host service, FINGERD, UNIX talk daemon and client
·Supports all of the Berkeley remote commands to Windows NT (RLOGIN, REXEC, RSH, RCP)
·Serial port logon to Windows NT command line
Product: The LPR/LPD kit for Windows NT
·Allows you to use all Windows NT hosted printers from a UNIX machine, and also all UNIX hosted printers from Windows NT and Windows for Workgroups which show up as regular Print Manager printers.
Product: Full feature NNTP news server for Windows NT
Product: Threaded News Reader for Windows NT
Product: Full SMTP/POP/UUCP/X.400 Gateway for Microsoft Mail for Windows NT
Product: SLIP and PPP kit for Windows NT (in beta - expected to ship in January '94)
Main: 1-800-946-6688
Fax: 1-515-232-7382
E-Mail: winnt@innov.com

Unix Source Code Control Systems

Mortice Kern Systems (MKS), Inc.
Products: MKS Toolkit for Windows NT
Main:  519-884-2251
Direct Sales: 800-265-2797
Fax: 519-884-8861

Congruent Corporation
Product: Toolbuster - CD-ROM loaded with GNU Utilities for Windows NT
·GNU toolset, compilers. libraries, debuggers, editors, and utilities
·on-line documentation
·executables for Intel, Alpha, and MIPS
·RCS component
Main:  212-431-5100
Fax:  212-219-1532
E-Mail: info@congruent.com

Hippo Software
Products: HIPPIX
·provides a library that supports over 90% of the functions of the POSIX 1003.1 API working with the Win32 subsystem
·provides a suite of over 100 UNIX utilities including: SH, VI, AWK, GREP, MAKE, LEX, YACC, RCS, ...
E-Mail: CompuServe: 72360,2675; Internet: hippix-info@hippo.com

Microsoft Windows API Support on UNIX

Bristol Technology
Product: Wind/U 1.3
·provides source code level support for MFC 2.0 and the Windows 16-bit API on UNIX Motif/X
Main: Jean Blackwell at 203-438-6969
E-Mail: jean@bristol.com

RPC Support

Windows 3.1 RPC client support and Windows NT RPC client and server support are included in the Windows NT SDK.

TCP/IP Support

A TCP/IP stack is included with Windows NT. The Windows NT SDK includes support for streams and the Windows Sockets interface.

Mail Support

You can connect from a Microsoft Mail server to UNIX mail using Microsoft Mail's SMTP gateway product. To connect from a Windows for Workgroups Post Office to UNIX mail using the SMTP gateway, you need to first upgrade your Windows for Workgroups™ Post Office using the Windows for Workgroups Microsoft Mail & Schedule+ Extensions Kit.

Dumb Terminal Support

Several third parties are working on various forms of dumb terminal support.

Multi-User Remote Access Services

Multiple users can log on remotely to a Windows NT Advanced Server machine using the Remote Access Services (RAS) software provided with that product. This software allows users to remotely connect to files stored on a Windows NT Advanced Server.

Windows NT SDK and Documentation Kit

The Windows NT SDK is available as part of the Microsoft Developer Network (MSDN) Level 2 CD-ROM subscription service. Included on the CD-ROM is on-line documentation, sample source code, includes all Microsoft Windows systems, all SDKs and all DDKs shipping worldwide, in one place. Call 800-759-5474 to order.

Developer Services and Beta Programs

To get development kits for a Microsoft Systems technology such as OLE 2.0, you should get the MSDN Level 2 CD-ROM subscription (see above). The ODBC SDK and many other developer materials can also be found on CompuServe's WINEXT forum. Microsoft Developer Services also has developer white papers and other support materials. To be considered for a Microsoft beta program you should call 206-936-BETA.

Books

Distributing Applications Across DCE and Windows NT by O'Reilly & Associates, Inc.
Main: 707.829.0515
Fax: 707.829.0104
E-Mail: order@ora.com

Windows NT Answer Book by Jim Groves, Microsoft Press

The Windows NT Resource Kit (3-volume set), Microsoft Press

Migrating to Windows NT by Randall C. Kennedy; Brady Publishing

Technical Support

CompuServe is the preferred vehicle for getting technical support for Windows NT and other Microsoft Systems technologies. CompuServe's MSWin32 has a section for porting from UNIX to Win32. Microsoft also offers fee-based technical support, including Premier Developer Support, Professional Support, and Electronic Technical Service Requests Packages. For more information, or to order support services, call Microsoft Developer Services at 800-227-4679, extension 11771.

Electronic Technical Service Requests

Electronic Technical Service Requests (SRs) packages give users the convenience of incremental information delivery and private responses from a Microsoft support engineer. SRs may be submitted 7 days a week, 24 hours a day, including holidays. Product Support Services (PSS) responds to SRs from Monday through Friday, 6 a.m. to 6 p.m., Pacific Time, excluding holidays.

Anonymous FTP Sites

ftp.microsoft.com Microsoft support files, patches, drivers, and other developer information.
rhino.microsoft.com Microsoft Windows NT TCP/IP applications and information.
ftp.cica.indiana.edu Shareware and free software for Microsoft Windows NT.
sunsite.unc.edu Shareware and free software for Microsoft Windows NT.
ftp.uu.net Developer code samples and information in the directory /vendor/microsoft.
ftp.iastate.edu FTPD, miscellaneous utilities as /pub/nt/[cpu-type]/ftpdserv.zip and other files. Also available on CompuServe forum MSWIN32 library 1 (New Uploads) files iftpd.zip and mftpd.zip.

Microsoft Resources

Microsoft Product Information: 800-227-4679 or 206-936-8661

Microsoft Developer Services: 800-227-4679, ext. 11771 [technical support packages, developer materials]

Microsoft Windows NT automated fax information: 800-936-4400

Microsoft Download Service Bulletin Board: 206-637-9009 (1200/2400/9600 bps modem–technical notes and support software)

Microsoft Developer Network CD-ROM subscription service: 800-759-5474 to order.