Appendix A: Best Practices

 

Important   The guidelines outlined in this appendix are not required to comply with the Application Specification for Windows 2000, but are strongly encouraged to provide a better user experience.

A-1  Installation of your application must not require a reboot on a freshly installed Windows 2000 system

Installation of your application should not require a reboot when you install it onto a freshly installed Windows 2000 system with no applications running unless you are deploying any of the following as part of your install.

  • Operating system update package, such as service pack
  • System-level drivers, such as 3rd party video drivers, disk drivers, and administrative level privileged services

Generally, a reboot on Windows 2000 should not be necessary, even if applications are running. In the past a typical reason apps would reboot was because they were replacing a file that was in use at the time. However, on Windows 2000, these situations are greatly eliminated when this Specification is adhered to because:

  • The operating system will protect essential files as part of Windows File Protection, so applications must not attempt to replace them; these files will be updated as a group via an operating system update package, instead of in a piecemeal fashion by individuals applications

  • Windows Installer service will automatically check to see if other applications or service processes are using files that it is attempting to update

    In these cases Windows installer will prompt the user to shutdown those apps that are using those files. If the user does this, the application will install without a reboot. If the user does not shut down those applications, Windows Installer will prompt for a reboot.

  • Applications that deploy side-by-side components will not impact existing copies of these components, since side-by-side components are private copies

A-2  Use SHGetFolderPath to determine special folder paths

Whenever you access any of the special folders in the following list, your application should use the Win32 APIs to dynamically obtain the proper language-specific folder names. The preferred way to do this is using the SHGetFolderPath API with the appropriate CSIDL constant. This function behaves consistently across Windows 95, Windows 98, Windows NT 4.0, and Windows 2000.

This API is redistributable via the SHFOLDER.DLL. SHFOLDER.DLL is installed by the Windows Installer redistributable. Software vendors are encouraged to redistribute this component as much as possible to enable this support on Windows operating systems prior to Windows 2000. Windows 2000 includes this DLL as a protected system file and, as such, this DLL cannot be replaced on Windows 2000 or greater.

To help ensure your application can run on Windows 9x, Windows NT 4 as well as Windows 2000, always link to the SHGetFolderPath implementation in SHFOLDER.DLL. Windows 2000 natively implements SHGetFolderPath in SHELL32.DLL, but other versions of Windows do not include SHGetFolderPath in SHELL32.DLL.

Standard Folder CSIDL Constant Name
Alternate Startup ([user], DBCS) CSIDL_ALTSTARTUP
Alternate Startup folder (All Users profile, DBCS) CSIDL_COMMON_ALTSTARTUP
Application Data ([user] profile) CSIDL_APPDATA
Application Data (All Users Profile) CSIDL_COMMON_APPDATA
Control Panel virtual folder CSIDL_CONTROLS
Cookies folder CSIDL_COOKIES
Desktop (namespace root) CSIDL_DESKTOP
Desktop folder ([user] profile) CSIDL_DESKTOPDIRECTORY
Desktop folder (All Users profile) CSIDL_COMMON_DESKTOPDIRECTORY
Favorites folder ([user] profile) CSIDL_FAVORITES
Favorites folder (All Users profile) CSIDL_COMMON_FAVORITES
Fonts virtual folder CSIDL_FONTS
History folder CSIDL_HISTORY
Internet Cache folder CSIDL_INTERNET_CACHE
Internet virtual folder CSIDL_INTERNET
Local (non-roaming) data repository for apps CSIDL_LOCAL_APPDATA
My Computer virtual folder CSIDL_DRIVES
My Pictures folder CSIDL_MYPICTURES
Network Neighborhood directory CSIDL_NETHOOD
Network Neighborhood root CSIDL_NETWORK
Personal folder ([user] profile) CSIDL_PERSONAL
Printers virtual folder CSIDL_PRINTERS
PrintHood folder ([user] profile) CSIDL_PRINTHOOD
Program Files folder CSIDL_PROGRAM_FILES
Program Files folder for x86 apps on Alpha systems CSIDL_PROGRAM_FILESX86
Programs folder (under Start menu in [user] profile) CSIDL_PROGRAMS
Programs folder (under Start menu in All Users profile) CSIDL_COMMON_PROGRAMS
Recent folder ([user] profile) CSIDL_RECENT
Recycle Bin folder CSIDL_BITBUCKET
SendTo folder ([user] profile) CSIDL_SENDTO
Start menu ([user] profile) CSIDL_STARTMENU
Start menu (All Users profile) CSIDL_COMMON_STARTMENU
Startup folder ([user] profile) CSIDL_STARTUP
Startup folder (All Users profile) CSIDL_COMMON_STARTUP
System folder CSIDL_SYSTEM
System folder for x86 applications on Alpha systems CSIDL_SYSTEMx86
Templates folder ([user] profile) CSIDL_TEMPLATES
User’s profile folder CSIDL_PROFILE
Windows directory or SYSROOT CSIDL_WINDOWS

A-3  Test your application under Terminal Services

Many enterprise customers use Terminal Services to provide Windows 2000 applications to an array of desktop and mobile clients. Applications running under terminal services run only on the server. The terminal server client performs no local processing of applications. This means that in a terminal services environment, multiple instances of the same application may be running on the same machine at the same time, serving different users.

Considerations for developers

  • Because multiple users are running your application at the same time, it’s critical to store application and user data properly, as outlined in "Chapter 4: Data and Settings Management"

  • In a Terminal Services environment, multiple instances of your application will be running on the same machine; your application’s .EXE and .DLL files must be written so multiple users can run your application on the same machine at the same time. Particular considerations include:

    • File locking:
      Ensure that files are not locked during use, as this could prevent multiple instances of the application, or processes under the application such as wizards, from running.
    • File permissions:
      Users may not have access to system files, and may not have the same permission levels as the administrator who installed the application.
    • File locations:
      Per user data and configuration files must be stored separately to avoid collisions and manage permissions. In particular, applications must store temporary information on a per user basis to avoid conflicts among users' information and preferences. You should do this by using the GetTempPath API rather than using a hard-coded path.
  • Terminal Services manages objects on a per-client basis for you

    You only need to think about object management, if you do not want the operating system to do this for you. If a specific object should be available to all instances of the application, register the .DLL or .EXE that creates the object, with the command "register filename /system", or by changing the application to append \\GLOBAL to the name of the object when it is created.

How to Pre-Test

  1. Set up a Windows 2000 Server computer as a Terminal Server Host and configure it to allow both administrator and non-administrator access.
  2. Install the application on this machine logged on as an Administrator.
    • The application must be installed in Terminal Server’s Install mode. Use the command line "change user /install" or use Add/Remove Programs in the Control Panel. This is required so that Terminal Server can appropriately manage the registry for each user. An installation script may also be required to adjust file and registry settings for multiple users.
  3. On a separate client machine, log on to the Terminal Server Host with normal (non-Power, non-Admin) User privileges.
  4. Ensure that all shortcuts, files and folders are available to the user.
  5. Launch each program in the application.
  6. Run a full set of functionality tests on your application.
  7. Create customized interface, file option and new default settings for the user.
  8. Log on as a different user and make sure that the setting changes you made for the first user are not the new defaults for the new user.
  9. Run a full set of functionality tests on your application simultaneously from two client computers as two different users.
  10. Run a full set of functionality tests on your application simultaneously from two client computers with one (non-Power, non-Admin) User and one Administrator.

Diagnosing Common Problems

  • If the problem was encountered while logged on to a client as a user without administrative rights, try to reproduce the problem on the Terminal Services server console as the same user. If the problem goes away, this indicates that the application is using a system resource or configuration. The console session has all system processes, such as SMSS.exe, RPCSS.exe, SERVICES.exe, and LSASS.exe, and the Windows NT services associated with it.
  • If the problem is still present on the Terminal Services server console, log on to a client machine as an Administrator and repeat the test. If the error does not occur, the problem is probably a file permission problem.
  • If the problem is still present, log on to the Terminal Services server console as the Administrator and repeat the test. If the error does not occur, the problem may be caused by incorrect permissions or the application is using a system resource or configuration.

References

A-4  Clients query Active Directory for Services

Any client that is locating a Windows 2000 service should query Active Directory to obtain binding information for the services that are of interest.

In Windows 2000, services publish their existence via objects in Active Directory. The objects contain binding information that applications use to connect to instances of the service. To access a service, an application does not need to know about specific computers; the objects in Active Directory include this information. An application queries Active Directory for an object representing a service (called a connection point object) and uses the binding information from the object to connect to the service.

In a distributed system, the computers are engines; the interesting entities are the services that are available. From the user's perspective, the identity of the computer that provides a particular service is not important. What is important is accessing the service itself.

To take advantage of the service-centric view afforded by the Active Directory Service, client applications must:

  • Query Active Directory for accessible services
  • Present these services to the end user or automatically select the appropriate service connection point object
  • Connect to the service using the binding information contained in the selected connection point object

For examples and more detailed information, see the section titled "Searching Active Directory" in the "Active Directory Programmer’s Guide" at: https://msdn.microsoft.com/developer/windows2000/adsi/actdirguide.asp.

A-5  Globalization

Globalization is the practice of designing and implementing software that is not locale dependent, i.e., can accommodate any locale. In software design, a locale is defined as a set of user preferences associated with a user’s language. A locale in Windows 2000 includes formats for date, time, currency and numbers; rules and tables for sorting and comparison; and tables of character classifications.

Other user preferences that a globalized application should accommodate include user-interface language, default font selection, language rules for use in spell checking and grammar, and input methods such as keyboard layouts and input method editors. See: https://www.microsoft.com/globaldev for more details.

Guidelines for developing a globalized application include the following:

  • Use Unicode as your character encoding to represent text; if your application must also run on Windows 9x, consider a method for using Unicode on those platforms

    If you cannot use Unicode, you will need to implement features such as DBCS enabling, BiDi enabling, codepage switching, and text tagging.

  • Consider using a multilingual user interface: launch the application in the default user interface language, and offer the option to change to other languages.

  • Use the Win32 API NLS functions to handle locale sensitive data

  • Watch for Windows messages that indicate changes in the input language, and use that information for spell checking, font selection, etc.

  • Use the Script APIs (Uniscribe) to layout formatted text on a page; this will allow your application to display multilingual text and complex scripts such as Arabic, Hebrew, Hindi, Tamil, and Thai

  • Test your application on in the following scenarios:

    • Where the system locale, the user locale, the input locale and the UI language each have different values and are all different from the localization language of the application
    • Where the system locale is not English (United States) and not the same as the application localization
    • Where the user locale is new in Windows 2000 (e.g. Hindi)
    • Where the UI language is one of the right-to-left languages such as Arabic
    • For a client/server application, where the client and server each have different locales
    • By using Regional Settings to customize the user locale (the more unusual the better for testing

A-6  Localizability

In contrast to globalization, localization is the process of modifying an application so that its user interface is in the language of the user. Well designed software can be localized to any of the languages supported by Windows 2000 without changes to the source code, i.e., without recompilation. In addition to the guidelines for globalization mentioned above, those for localizability include the following:

  1. Isolate all user interface elements from the program source code. Put them in resource files, message files, or a private database.
  2. Use the same resource identifiers throughout the life of the project. Changing identifiers makes it difficult to update localized resources from one build to another.
  3. Make multiple copies of the same string if it is used in multiple contexts. The same string may have different translations in different contexts.
  4. Do not place strings that should not be localized in resources. Leave them as string constants in the source code.
  5. Allocate text buffers dynamically, since text size may expand when translated. If you must use static buffers, make them extra large to accommodate localized strings (e.g., double the English string length).
  6. Keep in mind that dialog boxes may expand due to localization. Thus, a large dialog box that occupies the entire screen in low-resolution mode may have to be resized to an unusable size when localized.
  7. Avoid text in bitmaps and icons, as these are difficult to localize.
  8. Do not create a text message dynamically at runtime, either by concatenating multiple strings or by removing characters from static text. Word order varies by language, so dynamic composition of text in this manner requires code changes to localize to some languages.
  9. Similarly, avoid composing text using multiple insertion parameters in a format string (e.g., in sprintf or wsprintf), because the order of insertion of the arguments changes when translated to some languages.
  10. If localizing to a Middle Eastern language such as Arabic or Hebrew, use the right-to-left layout APIs to layout your application right to left.
  11. Test localized applications on all language variants of Windows 2000. If your application uses Unicode, as recommended, it should run with no modifications. If it uses a Windows codepage you will need to set the system locale to the appropriate value for your localized application, and reboot, before testing.

A-7  Additional Accessibility Considerations

Accessibility means accommodating the widest possible range of user needs and preferences, especially those that might prevent use by individuals with temporary or permanent impairments, seniors, and those working in unusual circumstances. You accomplish this goal by providing a flexible user interface and maintaining compatibility with external software utilities that provide additional functionality.

In particular, applications should do the following:

  • Allow other software to identify and manipulate all screen elements that the user interacts with. This should be accomplished by supporting Microsoft Active Accessibility. Standard controls provided by USER32.DLL and COMCTL32.DLL automatically support all of the required settings. Applications only need to add additional support when creating custom controls or when drawing content in their window client area. It is strongly recommended that vendors begin planning this support as soon as possible, and implement it in stages. You should test your application for proper behavior using the Narrator utility included with Windows 2000 and with the testing tools included in the Microsoft Active Accessibility SDK.
  • Allow the user to customize all user interface timings that are not based on standard system settings.
  • Be compatible with changes to the system font size and with changes to the number of pixels per logical inch.
  • Avoid triggering unexpected side effects based on changes in pointer or keyboard focus locations.
  • Avoid conveying important information by color alone. If color alone is the default method for conveying the information, the application should provide an option to convey this information by other means.

A-8  Use 64-bit compatible data types

It is possible for developers to use a single source-code base for their Win32- and Win64™-based applications. Microsoft has added this support by introducing new data types in the Platform SDK for Windows 2000.

There are three classes of new data types: fixed-precision data types, pointer-precision types, and specific-precision pointers. These types were added to the Windows environment (specifically, to Basetsd.h) to allow developers to prepare for 64-bit Windows well before its introduction. These new types were derived from the basic C-language integer and long types, so they work in existing code. Therefore, use these data types in your code now, test your code as a Win32-based application, and recompile as a Win64-based application when 64-bit Windows is available.

Fixed-precision

Fixed-precision data types are the same length in both Win32 and Win64 programming. To help you remember this, their precision is part of the name of the data type. The following are the fixed-precision data types.

Type Definition
DWORD32 32-bit unsigned integer
DWORD64 64-bit unsigned integer
INT32 32-bit signed integer
INT64 64-bit signed integer
LONG32 32-bit signed integer
LONG64 64-bit signed integer
UINT32 Unsigned INT32
UINT64 Unsigned INT64
ULONG32 Unsigned LONG32
ULONG64 Unsigned LONG64

Pointer Precision

As the pointer precision changes (that is, as it becomes 32 bits with Win32 code and 64 bits with Win64 code), these data types reflect the precision accordingly. Therefore, it is safe to cast a pointer to one of these types when performing pointer arithmetic; if the pointer precision is 64 bits, the type is 64 bits. The count types also reflect the maximum size to which a pointer can refer. The following are the pointer-precision and count types:

Type Definition
DWORD_PTR Unsigned long type for pointer precision
HALF_PTR Half the size of a pointer. Use within a structure that contains a pointer and two small fields
INT_PTR Signed integral type for pointer precision
LONG_PTR Signed long type for pointer precision
SIZE_T The maximum number of bytes to which a pointer can refer; use for a count that must span the full range of a pointer
SSIZE_T Signed SIZE_T
UHALF_PTR Unsigned HALF_PTR
UINT_PTR Unsigned INT_PTR
ULONG_PTR Unsigned LONG_PTR

Specific Pointer-Precision Types

There are also new pointer types that explicitly size the pointer. Be cautious when using pointers in 64-bit code: If you declare the pointer using a 32-bit type, the system creates the pointer by truncating a 64-bit pointer. (All pointers are 64 bits on a 64-bit platform.)

Type Definition
POINTER_32 A 32-bit pointer. On a 32-bit system, this is a native pointer.

On a 64-bit system, this is a truncated 64-bit pointer.

POINTER_64 A 64-bit pointer. On a 64-bit system, this is a native pointer.

On a 32-bit system, this is a sign-extended 32-bit pointer.

Note that it is not safe to assume the state of the high pointer bit.

For more details, please see the Microsoft Platform SDK or visit: https://msdn.microsoft.com/library/en-us/dnmidl/html/midl64b.asp.