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.
- File locking:
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
- Set up a Windows 2000 Server computer as a Terminal Server Host and configure it to allow both administrator and non-administrator access.
- 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.
- On a separate client machine, log on to the Terminal Server Host with normal (non-Power, non-Admin) User privileges.
- Ensure that all shortcuts, files and folders are available to the user.
- Launch each program in the application.
- Run a full set of functionality tests on your application.
- Create customized interface, file option and new default settings for the user.
- 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.
- Run a full set of functionality tests on your application simultaneously from two client computers as two different users.
- 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
- "Optimizing Applications for Windows 2000 Terminal Services and Windows NT Server 4.0, Terminal Server Edition" see: https://www.microsoft.com/windows/server/Technical/terminal/TSAppDev.asp
- "Using and Understanding APIs for Terminal Server" see: https://www.microsoft.com/ntserver/terminalserver/techdetails/prodarch/api.asp
- Using and Developing Applications Compatibility Scripts see: https://www.microsoft.com/ntserver/terminalserver/techdetails/prodarch/AppCompSc.asp
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:
- Isolate all user interface elements from the program source code. Put them in resource files, message files, or a private database.
- 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.
- Make multiple copies of the same string if it is used in multiple contexts. The same string may have different translations in different contexts.
- Do not place strings that should not be localized in resources. Leave them as string constants in the source code.
- 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).
- 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.
- Avoid text in bitmaps and icons, as these are difficult to localize.
- 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.
- 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.
- 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.
- 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.